]> git.openstreetmap.org Git - rails.git/blob - vendor/assets/iD/iD.js
Update to iD v2.18.5
[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 getDefaultExportFromCjs (x) {
5                 return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
6         }
7
8         function createCommonjsModule(fn, basedir, module) {
9                 return module = {
10                   path: basedir,
11                   exports: {},
12                   require: function (path, base) {
13               return commonjsRequire(path, (base === undefined || base === null) ? module.path : base);
14             }
15                 }, fn(module, module.exports), module.exports;
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 arguments$1 = arguments;
331
332                 var error, i, length = max$1(arguments.length, 2), assign;
333                 dest = Object(validValue(dest));
334                 assign = function (key) {
335                         try {
336                                 dest[key] = src[key];
337                         } catch (e) {
338                                 if (!error) { error = e; }
339                         }
340                 };
341                 for (i = 1; i < length; ++i) {
342                         src = arguments$1[i];
343                         keys$1(src).forEach(assign);
344                 }
345                 if (error !== undefined) { throw error; }
346                 return dest;
347         };
348
349         var assign = isImplemented$4() ? Object.assign : shim$5;
350
351         var forEach = Array.prototype.forEach, create$2 = Object.create;
352
353         var process$1 = function (src, obj) {
354                 var key;
355                 for (key in src) { obj[key] = src[key]; }
356         };
357
358         // eslint-disable-next-line no-unused-vars
359         var normalizeOptions = function (opts1/*, …options*/) {
360                 var result = create$2(null);
361                 forEach.call(arguments, function (options) {
362                         if (!isValue(options)) { return; }
363                         process$1(Object(options), result);
364                 });
365                 return result;
366         };
367
368         var str = "razdwatrzy";
369
370         var isImplemented$6 = function () {
371                 if (typeof str.contains !== "function") { return false; }
372                 return str.contains("dwa") === true && str.contains("foo") === false;
373         };
374
375         var indexOf$1 = String.prototype.indexOf;
376
377         var shim$6 = function (searchString/*, position*/) {
378                 return indexOf$1.call(this, searchString, arguments[1]) > -1;
379         };
380
381         var contains = isImplemented$6() ? String.prototype.contains : shim$6;
382
383         var d_1 = createCommonjsModule(function (module) {
384
385
386
387         var d = (module.exports = function (dscr, value/*, options*/) {
388                 var c, e, w, options, desc;
389                 if (arguments.length < 2 || typeof dscr !== "string") {
390                         options = value;
391                         value = dscr;
392                         dscr = null;
393                 } else {
394                         options = arguments[2];
395                 }
396                 if (is(dscr)) {
397                         c = contains.call(dscr, "c");
398                         e = contains.call(dscr, "e");
399                         w = contains.call(dscr, "w");
400                 } else {
401                         c = w = true;
402                         e = false;
403                 }
404
405                 desc = { value: value, configurable: c, enumerable: e, writable: w };
406                 return !options ? desc : assign(normalizeOptions(options), desc);
407         });
408
409         d.gs = function (dscr, get, set/*, options*/) {
410                 var c, e, options, desc;
411                 if (typeof dscr !== "string") {
412                         options = set;
413                         set = get;
414                         get = dscr;
415                         dscr = null;
416                 } else {
417                         options = arguments[3];
418                 }
419                 if (!is(get)) {
420                         get = undefined;
421                 } else if (!is$4(get)) {
422                         options = get;
423                         get = set = undefined;
424                 } else if (!is(set)) {
425                         set = undefined;
426                 } else if (!is$4(set)) {
427                         options = set;
428                         set = undefined;
429                 }
430                 if (is(dscr)) {
431                         c = contains.call(dscr, "c");
432                         e = contains.call(dscr, "e");
433                 } else {
434                         c = true;
435                         e = false;
436                 }
437
438                 desc = { get: get, set: set, configurable: c, enumerable: e };
439                 return !options ? desc : assign(normalizeOptions(options), desc);
440         };
441         });
442
443         var eventEmitter = createCommonjsModule(function (module, exports) {
444
445         var apply = Function.prototype.apply, call = Function.prototype.call
446           , create = Object.create, defineProperty = Object.defineProperty
447           , defineProperties = Object.defineProperties
448           , hasOwnProperty = Object.prototype.hasOwnProperty
449           , descriptor = { configurable: true, enumerable: false, writable: true }
450
451           , on, once, off, emit, methods, descriptors, base;
452
453         on = function (type, listener) {
454                 var data;
455
456                 validCallable(listener);
457
458                 if (!hasOwnProperty.call(this, '__ee__')) {
459                         data = descriptor.value = create(null);
460                         defineProperty(this, '__ee__', descriptor);
461                         descriptor.value = null;
462                 } else {
463                         data = this.__ee__;
464                 }
465                 if (!data[type]) { data[type] = listener; }
466                 else if (typeof data[type] === 'object') { data[type].push(listener); }
467                 else { data[type] = [data[type], listener]; }
468
469                 return this;
470         };
471
472         once = function (type, listener) {
473                 var once, self;
474
475                 validCallable(listener);
476                 self = this;
477                 on.call(this, type, once = function () {
478                         off.call(self, type, once);
479                         apply.call(listener, this, arguments);
480                 });
481
482                 once.__eeOnceListener__ = listener;
483                 return this;
484         };
485
486         off = function (type, listener) {
487                 var data, listeners, candidate, i;
488
489                 validCallable(listener);
490
491                 if (!hasOwnProperty.call(this, '__ee__')) { return this; }
492                 data = this.__ee__;
493                 if (!data[type]) { return this; }
494                 listeners = data[type];
495
496                 if (typeof listeners === 'object') {
497                         for (i = 0; (candidate = listeners[i]); ++i) {
498                                 if ((candidate === listener) ||
499                                                 (candidate.__eeOnceListener__ === listener)) {
500                                         if (listeners.length === 2) { data[type] = listeners[i ? 0 : 1]; }
501                                         else { listeners.splice(i, 1); }
502                                 }
503                         }
504                 } else {
505                         if ((listeners === listener) ||
506                                         (listeners.__eeOnceListener__ === listener)) {
507                                 delete data[type];
508                         }
509                 }
510
511                 return this;
512         };
513
514         emit = function (type) {
515                 var arguments$1 = arguments;
516
517                 var i, l, listener, listeners, args;
518
519                 if (!hasOwnProperty.call(this, '__ee__')) { return; }
520                 listeners = this.__ee__[type];
521                 if (!listeners) { return; }
522
523                 if (typeof listeners === 'object') {
524                         l = arguments.length;
525                         args = new Array(l - 1);
526                         for (i = 1; i < l; ++i) { args[i - 1] = arguments$1[i]; }
527
528                         listeners = listeners.slice();
529                         for (i = 0; (listener = listeners[i]); ++i) {
530                                 apply.call(listener, this, args);
531                         }
532                 } else {
533                         switch (arguments.length) {
534                         case 1:
535                                 call.call(listeners, this);
536                                 break;
537                         case 2:
538                                 call.call(listeners, this, arguments[1]);
539                                 break;
540                         case 3:
541                                 call.call(listeners, this, arguments[1], arguments[2]);
542                                 break;
543                         default:
544                                 l = arguments.length;
545                                 args = new Array(l - 1);
546                                 for (i = 1; i < l; ++i) {
547                                         args[i - 1] = arguments$1[i];
548                                 }
549                                 apply.call(listeners, this, args);
550                         }
551                 }
552         };
553
554         methods = {
555                 on: on,
556                 once: once,
557                 off: off,
558                 emit: emit
559         };
560
561         descriptors = {
562                 on: d_1(on),
563                 once: d_1(once),
564                 off: d_1(off),
565                 emit: d_1(emit)
566         };
567
568         base = defineProperties({}, descriptors);
569
570         module.exports = exports = function (o) {
571                 return (o == null) ? create(base) : defineProperties(Object(o), descriptors);
572         };
573         exports.methods = methods;
574         });
575
576         var validTypes = { object: true, symbol: true };
577
578         var isImplemented$7 = function () {
579                 var symbol;
580                 if (typeof Symbol !== 'function') { return false; }
581                 symbol = Symbol('test symbol');
582                 try { String(symbol); } catch (e) { return false; }
583
584                 // Return 'true' also for polyfills
585                 if (!validTypes[typeof Symbol.iterator]) { return false; }
586                 if (!validTypes[typeof Symbol.toPrimitive]) { return false; }
587                 if (!validTypes[typeof Symbol.toStringTag]) { return false; }
588
589                 return true;
590         };
591
592         var isSymbol = function (x) {
593                 if (!x) { return false; }
594                 if (typeof x === 'symbol') { return true; }
595                 if (!x.constructor) { return false; }
596                 if (x.constructor.name !== 'Symbol') { return false; }
597                 return (x[x.constructor.toStringTag] === 'Symbol');
598         };
599
600         var validateSymbol = function (value) {
601                 if (!isSymbol(value)) { throw new TypeError(value + " is not a symbol"); }
602                 return value;
603         };
604
605         var create$3 = Object.create, defineProperties = Object.defineProperties
606           , defineProperty$1 = Object.defineProperty, objPrototype = Object.prototype
607           , NativeSymbol, SymbolPolyfill, HiddenSymbol, globalSymbols = create$3(null)
608           , isNativeSafe;
609
610         if (typeof Symbol === 'function') {
611                 NativeSymbol = Symbol;
612                 try {
613                         String(NativeSymbol());
614                         isNativeSafe = true;
615                 } catch (ignore) {}
616         }
617
618         var generateName = (function () {
619                 var created = create$3(null);
620                 return function (desc) {
621                         var postfix = 0, name, ie11BugWorkaround;
622                         while (created[desc + (postfix || '')]) { ++postfix; }
623                         desc += (postfix || '');
624                         created[desc] = true;
625                         name = '@@' + desc;
626                         defineProperty$1(objPrototype, name, d_1.gs(null, function (value) {
627                                 // For IE11 issue see:
628                                 // https://connect.microsoft.com/IE/feedbackdetail/view/1928508/
629                                 //    ie11-broken-getters-on-dom-objects
630                                 // https://github.com/medikoo/es6-symbol/issues/12
631                                 if (ie11BugWorkaround) { return; }
632                                 ie11BugWorkaround = true;
633                                 defineProperty$1(this, name, d_1(value));
634                                 ie11BugWorkaround = false;
635                         }));
636                         return name;
637                 };
638         }());
639
640         // Internal constructor (not one exposed) for creating Symbol instances.
641         // This one is used to ensure that `someSymbol instanceof Symbol` always return false
642         HiddenSymbol = function Symbol(description) {
643                 if (this instanceof HiddenSymbol) { throw new TypeError('Symbol is not a constructor'); }
644                 return SymbolPolyfill(description);
645         };
646
647         // Exposed `Symbol` constructor
648         // (returns instances of HiddenSymbol)
649         var polyfill = SymbolPolyfill = function Symbol(description) {
650                 var symbol;
651                 if (this instanceof Symbol) { throw new TypeError('Symbol is not a constructor'); }
652                 if (isNativeSafe) { return NativeSymbol(description); }
653                 symbol = create$3(HiddenSymbol.prototype);
654                 description = (description === undefined ? '' : String(description));
655                 return defineProperties(symbol, {
656                         __description__: d_1('', description),
657                         __name__: d_1('', generateName(description))
658                 });
659         };
660         defineProperties(SymbolPolyfill, {
661                 for: d_1(function (key) {
662                         if (globalSymbols[key]) { return globalSymbols[key]; }
663                         return (globalSymbols[key] = SymbolPolyfill(String(key)));
664                 }),
665                 keyFor: d_1(function (s) {
666                         var key;
667                         validateSymbol(s);
668                         for (key in globalSymbols) { if (globalSymbols[key] === s) { return key; } }
669                 }),
670
671                 // To ensure proper interoperability with other native functions (e.g. Array.from)
672                 // fallback to eventual native implementation of given symbol
673                 hasInstance: d_1('', (NativeSymbol && NativeSymbol.hasInstance) || SymbolPolyfill('hasInstance')),
674                 isConcatSpreadable: d_1('', (NativeSymbol && NativeSymbol.isConcatSpreadable) ||
675                         SymbolPolyfill('isConcatSpreadable')),
676                 iterator: d_1('', (NativeSymbol && NativeSymbol.iterator) || SymbolPolyfill('iterator')),
677                 match: d_1('', (NativeSymbol && NativeSymbol.match) || SymbolPolyfill('match')),
678                 replace: d_1('', (NativeSymbol && NativeSymbol.replace) || SymbolPolyfill('replace')),
679                 search: d_1('', (NativeSymbol && NativeSymbol.search) || SymbolPolyfill('search')),
680                 species: d_1('', (NativeSymbol && NativeSymbol.species) || SymbolPolyfill('species')),
681                 split: d_1('', (NativeSymbol && NativeSymbol.split) || SymbolPolyfill('split')),
682                 toPrimitive: d_1('', (NativeSymbol && NativeSymbol.toPrimitive) || SymbolPolyfill('toPrimitive')),
683                 toStringTag: d_1('', (NativeSymbol && NativeSymbol.toStringTag) || SymbolPolyfill('toStringTag')),
684                 unscopables: d_1('', (NativeSymbol && NativeSymbol.unscopables) || SymbolPolyfill('unscopables'))
685         });
686
687         // Internal tweaks for real symbol producer
688         defineProperties(HiddenSymbol.prototype, {
689                 constructor: d_1(SymbolPolyfill),
690                 toString: d_1('', function () { return this.__name__; })
691         });
692
693         // Proper implementation of methods exposed on Symbol.prototype
694         // They won't be accessible on produced symbol instances as they derive from HiddenSymbol.prototype
695         defineProperties(SymbolPolyfill.prototype, {
696                 toString: d_1(function () { return 'Symbol (' + validateSymbol(this).__description__ + ')'; }),
697                 valueOf: d_1(function () { return validateSymbol(this); })
698         });
699         defineProperty$1(SymbolPolyfill.prototype, SymbolPolyfill.toPrimitive, d_1('', function () {
700                 var symbol = validateSymbol(this);
701                 if (typeof symbol === 'symbol') { return symbol; }
702                 return symbol.toString();
703         }));
704         defineProperty$1(SymbolPolyfill.prototype, SymbolPolyfill.toStringTag, d_1('c', 'Symbol'));
705
706         // Proper implementaton of toPrimitive and toStringTag for returned symbol instances
707         defineProperty$1(HiddenSymbol.prototype, SymbolPolyfill.toStringTag,
708                 d_1('c', SymbolPolyfill.prototype[SymbolPolyfill.toStringTag]));
709
710         // Note: It's important to define `toPrimitive` as last one, as some implementations
711         // implement `toPrimitive` natively without implementing `toStringTag` (or other specified symbols)
712         // And that may invoke error in definition flow:
713         // See: https://github.com/medikoo/es6-symbol/issues/13#issuecomment-164146149
714         defineProperty$1(HiddenSymbol.prototype, SymbolPolyfill.toPrimitive,
715                 d_1('c', SymbolPolyfill.prototype[SymbolPolyfill.toPrimitive]));
716
717         var es6Symbol = isImplemented$7() ? Symbol : polyfill;
718
719         var objToString = Object.prototype.toString
720           , id = objToString.call((function () { return arguments; })());
721
722         var isArguments = function (value) { return objToString.call(value) === id; };
723
724         var objToString$1 = Object.prototype.toString, id$1 = objToString$1.call("");
725
726         var isString = function (value) {
727                 return (
728                         typeof value === "string" ||
729                         (value &&
730                                 typeof value === "object" &&
731                                 (value instanceof String || objToString$1.call(value) === id$1)) ||
732                         false
733                 );
734         };
735
736         var isImplemented$8 = function () {
737                 if (typeof globalThis !== "object") { return false; }
738                 if (!globalThis) { return false; }
739                 return globalThis.Array === Array;
740         };
741
742         var naiveFallback = function () {
743                 if (typeof self === "object" && self) { return self; }
744                 if (typeof window === "object" && window) { return window; }
745                 throw new Error("Unable to resolve global `this`");
746         };
747
748         var implementation = (function () {
749                 if (this) { return this; }
750
751                 // Unexpected strict mode (may happen if e.g. bundled into ESM module)
752
753                 // Thanks @mathiasbynens -> https://mathiasbynens.be/notes/globalthis
754                 // In all ES5+ engines global object inherits from Object.prototype
755                 // (if you approached one that doesn't please report)
756                 try {
757                         Object.defineProperty(Object.prototype, "__global__", {
758                                 get: function () { return this; },
759                                 configurable: true
760                         });
761                 } catch (error) {
762                         // Unfortunate case of Object.prototype being sealed (via preventExtensions, seal or freeze)
763                         return naiveFallback();
764                 }
765                 try {
766                         // Safari case (window.__global__ is resolved with global context, but __global__ does not)
767                         if (!__global__) { return naiveFallback(); }
768                         return __global__;
769                 } finally {
770                         delete Object.prototype.__global__;
771                 }
772         })();
773
774         var globalThis_1 = isImplemented$8() ? globalThis : implementation;
775
776         var validTypes$1 = { object: true, symbol: true };
777
778         var isImplemented$9 = function () {
779                 var Symbol = globalThis_1.Symbol;
780                 var symbol;
781                 if (typeof Symbol !== "function") { return false; }
782                 symbol = Symbol("test symbol");
783                 try { String(symbol); }
784                 catch (e) { return false; }
785
786                 // Return 'true' also for polyfills
787                 if (!validTypes$1[typeof Symbol.iterator]) { return false; }
788                 if (!validTypes$1[typeof Symbol.toPrimitive]) { return false; }
789                 if (!validTypes$1[typeof Symbol.toStringTag]) { return false; }
790
791                 return true;
792         };
793
794         var isSymbol$1 = function (value) {
795                 if (!value) { return false; }
796                 if (typeof value === "symbol") { return true; }
797                 if (!value.constructor) { return false; }
798                 if (value.constructor.name !== "Symbol") { return false; }
799                 return value[value.constructor.toStringTag] === "Symbol";
800         };
801
802         var validateSymbol$1 = function (value) {
803                 if (!isSymbol$1(value)) { throw new TypeError(value + " is not a symbol"); }
804                 return value;
805         };
806
807         var create$4 = Object.create, defineProperty$2 = Object.defineProperty, objPrototype$1 = Object.prototype;
808
809         var created = create$4(null);
810         var generateName$1 = function (desc) {
811                 var postfix = 0, name, ie11BugWorkaround;
812                 while (created[desc + (postfix || "")]) { ++postfix; }
813                 desc += postfix || "";
814                 created[desc] = true;
815                 name = "@@" + desc;
816                 defineProperty$2(
817                         objPrototype$1,
818                         name,
819                         d_1.gs(null, function (value) {
820                                 // For IE11 issue see:
821                                 // https://connect.microsoft.com/IE/feedbackdetail/view/1928508/
822                                 //    ie11-broken-getters-on-dom-objects
823                                 // https://github.com/medikoo/es6-symbol/issues/12
824                                 if (ie11BugWorkaround) { return; }
825                                 ie11BugWorkaround = true;
826                                 defineProperty$2(this, name, d_1(value));
827                                 ie11BugWorkaround = false;
828                         })
829                 );
830                 return name;
831         };
832
833         var NativeSymbol$1 = globalThis_1.Symbol;
834
835         var standardSymbols = function (SymbolPolyfill) {
836                 return Object.defineProperties(SymbolPolyfill, {
837                         // To ensure proper interoperability with other native functions (e.g. Array.from)
838                         // fallback to eventual native implementation of given symbol
839                         hasInstance: d_1(
840                                 "", (NativeSymbol$1 && NativeSymbol$1.hasInstance) || SymbolPolyfill("hasInstance")
841                         ),
842                         isConcatSpreadable: d_1(
843                                 "",
844                                 (NativeSymbol$1 && NativeSymbol$1.isConcatSpreadable) ||
845                                         SymbolPolyfill("isConcatSpreadable")
846                         ),
847                         iterator: d_1("", (NativeSymbol$1 && NativeSymbol$1.iterator) || SymbolPolyfill("iterator")),
848                         match: d_1("", (NativeSymbol$1 && NativeSymbol$1.match) || SymbolPolyfill("match")),
849                         replace: d_1("", (NativeSymbol$1 && NativeSymbol$1.replace) || SymbolPolyfill("replace")),
850                         search: d_1("", (NativeSymbol$1 && NativeSymbol$1.search) || SymbolPolyfill("search")),
851                         species: d_1("", (NativeSymbol$1 && NativeSymbol$1.species) || SymbolPolyfill("species")),
852                         split: d_1("", (NativeSymbol$1 && NativeSymbol$1.split) || SymbolPolyfill("split")),
853                         toPrimitive: d_1(
854                                 "", (NativeSymbol$1 && NativeSymbol$1.toPrimitive) || SymbolPolyfill("toPrimitive")
855                         ),
856                         toStringTag: d_1(
857                                 "", (NativeSymbol$1 && NativeSymbol$1.toStringTag) || SymbolPolyfill("toStringTag")
858                         ),
859                         unscopables: d_1(
860                                 "", (NativeSymbol$1 && NativeSymbol$1.unscopables) || SymbolPolyfill("unscopables")
861                         )
862                 });
863         };
864
865         var registry = Object.create(null);
866
867         var symbolRegistry = function (SymbolPolyfill) {
868                 return Object.defineProperties(SymbolPolyfill, {
869                         for: d_1(function (key) {
870                                 if (registry[key]) { return registry[key]; }
871                                 return (registry[key] = SymbolPolyfill(String(key)));
872                         }),
873                         keyFor: d_1(function (symbol) {
874                                 var key;
875                                 validateSymbol$1(symbol);
876                                 for (key in registry) {
877                                         if (registry[key] === symbol) { return key; }
878                                 }
879                                 return undefined;
880                         })
881                 });
882         };
883
884         var NativeSymbol$2         = globalThis_1.Symbol;
885
886         var create$5 = Object.create
887           , defineProperties$1 = Object.defineProperties
888           , defineProperty$3 = Object.defineProperty;
889
890         var SymbolPolyfill$1, HiddenSymbol$1, isNativeSafe$1;
891
892         if (typeof NativeSymbol$2 === "function") {
893                 try {
894                         String(NativeSymbol$2());
895                         isNativeSafe$1 = true;
896                 } catch (ignore) {}
897         } else {
898                 NativeSymbol$2 = null;
899         }
900
901         // Internal constructor (not one exposed) for creating Symbol instances.
902         // This one is used to ensure that `someSymbol instanceof Symbol` always return false
903         HiddenSymbol$1 = function Symbol(description) {
904                 if (this instanceof HiddenSymbol$1) { throw new TypeError("Symbol is not a constructor"); }
905                 return SymbolPolyfill$1(description);
906         };
907
908         // Exposed `Symbol` constructor
909         // (returns instances of HiddenSymbol)
910         var polyfill$1 = SymbolPolyfill$1 = function Symbol(description) {
911                 var symbol;
912                 if (this instanceof Symbol) { throw new TypeError("Symbol is not a constructor"); }
913                 if (isNativeSafe$1) { return NativeSymbol$2(description); }
914                 symbol = create$5(HiddenSymbol$1.prototype);
915                 description = description === undefined ? "" : String(description);
916                 return defineProperties$1(symbol, {
917                         __description__: d_1("", description),
918                         __name__: d_1("", generateName$1(description))
919                 });
920         };
921
922         standardSymbols(SymbolPolyfill$1);
923         symbolRegistry(SymbolPolyfill$1);
924
925         // Internal tweaks for real symbol producer
926         defineProperties$1(HiddenSymbol$1.prototype, {
927                 constructor: d_1(SymbolPolyfill$1),
928                 toString: d_1("", function () { return this.__name__; })
929         });
930
931         // Proper implementation of methods exposed on Symbol.prototype
932         // They won't be accessible on produced symbol instances as they derive from HiddenSymbol.prototype
933         defineProperties$1(SymbolPolyfill$1.prototype, {
934                 toString: d_1(function () { return "Symbol (" + validateSymbol$1(this).__description__ + ")"; }),
935                 valueOf: d_1(function () { return validateSymbol$1(this); })
936         });
937         defineProperty$3(
938                 SymbolPolyfill$1.prototype,
939                 SymbolPolyfill$1.toPrimitive,
940                 d_1("", function () {
941                         var symbol = validateSymbol$1(this);
942                         if (typeof symbol === "symbol") { return symbol; }
943                         return symbol.toString();
944                 })
945         );
946         defineProperty$3(SymbolPolyfill$1.prototype, SymbolPolyfill$1.toStringTag, d_1("c", "Symbol"));
947
948         // Proper implementaton of toPrimitive and toStringTag for returned symbol instances
949         defineProperty$3(
950                 HiddenSymbol$1.prototype, SymbolPolyfill$1.toStringTag,
951                 d_1("c", SymbolPolyfill$1.prototype[SymbolPolyfill$1.toStringTag])
952         );
953
954         // Note: It's important to define `toPrimitive` as last one, as some implementations
955         // implement `toPrimitive` natively without implementing `toStringTag` (or other specified symbols)
956         // And that may invoke error in definition flow:
957         // See: https://github.com/medikoo/es6-symbol/issues/13#issuecomment-164146149
958         defineProperty$3(
959                 HiddenSymbol$1.prototype, SymbolPolyfill$1.toPrimitive,
960                 d_1("c", SymbolPolyfill$1.prototype[SymbolPolyfill$1.toPrimitive])
961         );
962
963         var es6Symbol$1 = isImplemented$9()
964                 ? globalThis_1.Symbol
965                 : polyfill$1;
966
967         var iteratorSymbol = es6Symbol$1.iterator
968           , isArray        = Array.isArray;
969
970         var isIterable = function (value) {
971                 if (!isValue(value)) { return false; }
972                 if (isArray(value)) { return true; }
973                 if (isString(value)) { return true; }
974                 if (isArguments(value)) { return true; }
975                 return typeof value[iteratorSymbol] === "function";
976         };
977
978         var validIterable = function (value) {
979                 if (!isIterable(value)) { throw new TypeError(value + " is not iterable"); }
980                 return value;
981         };
982
983         var objectToString = Object.prototype.toString;
984
985         var coerce = function (value) {
986                 if (!is(value)) { return null; }
987                 if (is$1(value)) {
988                         // Reject Object.prototype.toString coercion
989                         var valueToString = value.toString;
990                         if (typeof valueToString !== "function") { return null; }
991                         if (valueToString === objectToString) { return null; }
992                         // Note: It can be object coming from other realm, still as there's no ES3 and CSP compliant
993                         // way to resolve its realm's Object.prototype.toString it's left as not addressed edge case
994                 }
995                 try {
996                         return "" + value; // Ensure implicit coercion
997                 } catch (error) {
998                         return null;
999                 }
1000         };
1001
1002         var safeToString = function (value) {
1003                 try {
1004                         return value.toString();
1005                 } catch (error) {
1006                         try { return String(value); }
1007                         catch (error2) { return null; }
1008                 }
1009         };
1010
1011         var reNewLine = /[\n\r\u2028\u2029]/g;
1012
1013         var toShortString = function (value) {
1014                 var string = safeToString(value);
1015                 if (string === null) { return "<Non-coercible to string value>"; }
1016                 // Trim if too long
1017                 if (string.length > 100) { string = string.slice(0, 99) + "…"; }
1018                 // Replace eventual new lines
1019                 string = string.replace(reNewLine, function (char) {
1020                         switch (char) {
1021                                 case "\n":
1022                                         return "\\n";
1023                                 case "\r":
1024                                         return "\\r";
1025                                 case "\u2028":
1026                                         return "\\u2028";
1027                                 case "\u2029":
1028                                         return "\\u2029";
1029                                 /* istanbul ignore next */
1030                                 default:
1031                                         throw new Error("Unexpected character");
1032                         }
1033                 });
1034                 return string;
1035         };
1036
1037         var resolveMessage = function (message, value) {
1038                 return message.replace("%v", toShortString(value));
1039         };
1040
1041         var resolveException = function (value, defaultMessage, inputOptions) {
1042                 if (!is$1(inputOptions)) { throw new TypeError(resolveMessage(defaultMessage, value)); }
1043                 if (!is(value)) {
1044                         if ("default" in inputOptions) { return inputOptions["default"]; }
1045                         if (inputOptions.isOptional) { return null; }
1046                 }
1047                 var errorMessage = coerce(inputOptions.errorMessage);
1048                 if (!is(errorMessage)) { errorMessage = defaultMessage; }
1049                 throw new TypeError(resolveMessage(errorMessage, value));
1050         };
1051
1052         var ensure = function (value/*, options*/) {
1053                 if (is(value)) { return value; }
1054                 return resolveException(value, "Cannot use %v", arguments[1]);
1055         };
1056
1057         var ensure$1 = function (value/*, options*/) {
1058                 if (is$4(value)) { return value; }
1059                 return resolveException(value, "%v is not a plain function", arguments[1]);
1060         };
1061
1062         var isImplemented$a = function () {
1063                 var from = Array.from, arr, result;
1064                 if (typeof from !== "function") { return false; }
1065                 arr = ["raz", "dwa"];
1066                 result = from(arr);
1067                 return Boolean(result && result !== arr && result[1] === "dwa");
1068         };
1069
1070         var objToString$2 = Object.prototype.toString
1071           , isFunctionStringTag = RegExp.prototype.test.bind(/^[object [A-Za-z0-9]*Function]$/);
1072
1073         var isFunction = function (value) {
1074                 return typeof value === "function" && isFunctionStringTag(objToString$2.call(value));
1075         };
1076
1077         var iteratorSymbol$1 = es6Symbol$1.iterator
1078           , isArray$1        = Array.isArray
1079           , call           = Function.prototype.call
1080           , desc           = { configurable: true, enumerable: true, writable: true, value: null }
1081           , defineProperty$4 = Object.defineProperty;
1082
1083         // eslint-disable-next-line complexity, max-lines-per-function
1084         var shim$7 = function (arrayLike/*, mapFn, thisArg*/) {
1085                 var mapFn = arguments[1]
1086                   , thisArg = arguments[2]
1087                   , Context
1088                   , i
1089                   , j
1090                   , arr
1091                   , length
1092                   , code
1093                   , iterator
1094                   , result
1095                   , getIterator
1096                   , value;
1097
1098                 arrayLike = Object(validValue(arrayLike));
1099
1100                 if (isValue(mapFn)) { validCallable(mapFn); }
1101                 if (!this || this === Array || !isFunction(this)) {
1102                         // Result: Plain array
1103                         if (!mapFn) {
1104                                 if (isArguments(arrayLike)) {
1105                                         // Source: Arguments
1106                                         length = arrayLike.length;
1107                                         if (length !== 1) { return Array.apply(null, arrayLike); }
1108                                         arr = new Array(1);
1109                                         arr[0] = arrayLike[0];
1110                                         return arr;
1111                                 }
1112                                 if (isArray$1(arrayLike)) {
1113                                         // Source: Array
1114                                         arr = new Array((length = arrayLike.length));
1115                                         for (i = 0; i < length; ++i) { arr[i] = arrayLike[i]; }
1116                                         return arr;
1117                                 }
1118                         }
1119                         arr = [];
1120                 } else {
1121                         // Result: Non plain array
1122                         Context = this;
1123                 }
1124
1125                 if (!isArray$1(arrayLike)) {
1126                         if ((getIterator = arrayLike[iteratorSymbol$1]) !== undefined) {
1127                                 // Source: Iterator
1128                                 iterator = validCallable(getIterator).call(arrayLike);
1129                                 if (Context) { arr = new Context(); }
1130                                 result = iterator.next();
1131                                 i = 0;
1132                                 while (!result.done) {
1133                                         value = mapFn ? call.call(mapFn, thisArg, result.value, i) : result.value;
1134                                         if (Context) {
1135                                                 desc.value = value;
1136                                                 defineProperty$4(arr, i, desc);
1137                                         } else {
1138                                                 arr[i] = value;
1139                                         }
1140                                         result = iterator.next();
1141                                         ++i;
1142                                 }
1143                                 length = i;
1144                         } else if (isString(arrayLike)) {
1145                                 // Source: String
1146                                 length = arrayLike.length;
1147                                 if (Context) { arr = new Context(); }
1148                                 for (i = 0, j = 0; i < length; ++i) {
1149                                         value = arrayLike[i];
1150                                         if (i + 1 < length) {
1151                                                 code = value.charCodeAt(0);
1152                                                 // eslint-disable-next-line max-depth
1153                                                 if (code >= 0xd800 && code <= 0xdbff) { value += arrayLike[++i]; }
1154                                         }
1155                                         value = mapFn ? call.call(mapFn, thisArg, value, j) : value;
1156                                         if (Context) {
1157                                                 desc.value = value;
1158                                                 defineProperty$4(arr, j, desc);
1159                                         } else {
1160                                                 arr[j] = value;
1161                                         }
1162                                         ++j;
1163                                 }
1164                                 length = j;
1165                         }
1166                 }
1167                 if (length === undefined) {
1168                         // Source: array or array-like
1169                         length = toPosInteger(arrayLike.length);
1170                         if (Context) { arr = new Context(length); }
1171                         for (i = 0; i < length; ++i) {
1172                                 value = mapFn ? call.call(mapFn, thisArg, arrayLike[i], i) : arrayLike[i];
1173                                 if (Context) {
1174                                         desc.value = value;
1175                                         defineProperty$4(arr, i, desc);
1176                                 } else {
1177                                         arr[i] = value;
1178                                 }
1179                         }
1180                 }
1181                 if (Context) {
1182                         desc.value = null;
1183                         arr.length = length;
1184                 }
1185                 return arr;
1186         };
1187
1188         var from_1 = isImplemented$a() ? Array.from : shim$7;
1189
1190         var copy = function (obj/*, propertyNames, options*/) {
1191                 var copy = Object(validValue(obj)), propertyNames = arguments[1], options = Object(arguments[2]);
1192                 if (copy !== obj && !propertyNames) { return copy; }
1193                 var result = {};
1194                 if (propertyNames) {
1195                         from_1(propertyNames, function (propertyName) {
1196                                 if (options.ensure || propertyName in obj) { result[propertyName] = obj[propertyName]; }
1197                         });
1198                 } else {
1199                         assign(result, obj);
1200                 }
1201                 return result;
1202         };
1203
1204         var bind                    = Function.prototype.bind
1205           , call$1                    = Function.prototype.call
1206           , keys$2                    = Object.keys
1207           , objPropertyIsEnumerable = Object.prototype.propertyIsEnumerable;
1208
1209         var _iterate = function (method, defVal) {
1210                 return function (obj, cb/*, thisArg, compareFn*/) {
1211                         var list, thisArg = arguments[2], compareFn = arguments[3];
1212                         obj = Object(validValue(obj));
1213                         validCallable(cb);
1214
1215                         list = keys$2(obj);
1216                         if (compareFn) {
1217                                 list.sort(typeof compareFn === "function" ? bind.call(compareFn, obj) : undefined);
1218                         }
1219                         if (typeof method !== "function") { method = list[method]; }
1220                         return call$1.call(method, list, function (key, index) {
1221                                 if (!objPropertyIsEnumerable.call(obj, key)) { return defVal; }
1222                                 return call$1.call(cb, thisArg, obj[key], key, obj, index);
1223                         });
1224                 };
1225         };
1226
1227         var forEach$1 = _iterate("forEach");
1228
1229         var call$2     = Function.prototype.call;
1230
1231         var map$1 = function (obj, cb/*, thisArg*/) {
1232                 var result = {}, thisArg = arguments[2];
1233                 validCallable(cb);
1234                 forEach$1(obj, function (value, key, targetObj, index) {
1235                         result[key] = call$2.call(cb, thisArg, value, key, targetObj, index);
1236                 });
1237                 return result;
1238         };
1239
1240         var bind$1 = Function.prototype.bind
1241           , defineProperty$5 = Object.defineProperty
1242           , hasOwnProperty$1 = Object.prototype.hasOwnProperty
1243           , define;
1244
1245         define = function (name, desc, options) {
1246                 var value = ensure(desc) && ensure$1(desc.value), dgs;
1247                 dgs = copy(desc);
1248                 delete dgs.writable;
1249                 delete dgs.value;
1250                 dgs.get = function () {
1251                         if (!options.overwriteDefinition && hasOwnProperty$1.call(this, name)) { return value; }
1252                         desc.value = bind$1.call(value, options.resolveContext ? options.resolveContext(this) : this);
1253                         defineProperty$5(this, name, desc);
1254                         return this[name];
1255                 };
1256                 return dgs;
1257         };
1258
1259         var autoBind = function (props/*, options*/) {
1260                 var options = normalizeOptions(arguments[1]);
1261                 if (is(options.resolveContext)) { ensure$1(options.resolveContext); }
1262                 return map$1(props, function (desc, name) { return define(name, desc, options); });
1263         };
1264
1265         var defineProperty$6 = Object.defineProperty, defineProperties$2 = Object.defineProperties, Iterator;
1266
1267         var es6Iterator = Iterator = function (list, context) {
1268                 if (!(this instanceof Iterator)) { throw new TypeError("Constructor requires 'new'"); }
1269                 defineProperties$2(this, {
1270                         __list__: d_1("w", validValue(list)),
1271                         __context__: d_1("w", context),
1272                         __nextIndex__: d_1("w", 0)
1273                 });
1274                 if (!context) { return; }
1275                 validCallable(context.on);
1276                 context.on("_add", this._onAdd);
1277                 context.on("_delete", this._onDelete);
1278                 context.on("_clear", this._onClear);
1279         };
1280
1281         // Internal %IteratorPrototype% doesn't expose its constructor
1282         delete Iterator.prototype.constructor;
1283
1284         defineProperties$2(
1285                 Iterator.prototype,
1286                 assign(
1287                         {
1288                                 _next: d_1(function () {
1289                                         var i;
1290                                         if (!this.__list__) { return undefined; }
1291                                         if (this.__redo__) {
1292                                                 i = this.__redo__.shift();
1293                                                 if (i !== undefined) { return i; }
1294                                         }
1295                                         if (this.__nextIndex__ < this.__list__.length) { return this.__nextIndex__++; }
1296                                         this._unBind();
1297                                         return undefined;
1298                                 }),
1299                                 next: d_1(function () {
1300                                         return this._createResult(this._next());
1301                                 }),
1302                                 _createResult: d_1(function (i) {
1303                                         if (i === undefined) { return { done: true, value: undefined }; }
1304                                         return { done: false, value: this._resolve(i) };
1305                                 }),
1306                                 _resolve: d_1(function (i) {
1307                                         return this.__list__[i];
1308                                 }),
1309                                 _unBind: d_1(function () {
1310                                         this.__list__ = null;
1311                                         delete this.__redo__;
1312                                         if (!this.__context__) { return; }
1313                                         this.__context__.off("_add", this._onAdd);
1314                                         this.__context__.off("_delete", this._onDelete);
1315                                         this.__context__.off("_clear", this._onClear);
1316                                         this.__context__ = null;
1317                                 }),
1318                                 toString: d_1(function () {
1319                                         return "[object " + (this[es6Symbol$1.toStringTag] || "Object") + "]";
1320                                 })
1321                         },
1322                         autoBind({
1323                                 _onAdd: d_1(function (index) {
1324                                         if (index >= this.__nextIndex__) { return; }
1325                                         ++this.__nextIndex__;
1326                                         if (!this.__redo__) {
1327                                                 defineProperty$6(this, "__redo__", d_1("c", [index]));
1328                                                 return;
1329                                         }
1330                                         this.__redo__.forEach(function (redo, i) {
1331                                                 if (redo >= index) { this.__redo__[i] = ++redo; }
1332                                         }, this);
1333                                         this.__redo__.push(index);
1334                                 }),
1335                                 _onDelete: d_1(function (index) {
1336                                         var i;
1337                                         if (index >= this.__nextIndex__) { return; }
1338                                         --this.__nextIndex__;
1339                                         if (!this.__redo__) { return; }
1340                                         i = this.__redo__.indexOf(index);
1341                                         if (i !== -1) { this.__redo__.splice(i, 1); }
1342                                         this.__redo__.forEach(function (redo, j) {
1343                                                 if (redo > index) { this.__redo__[j] = --redo; }
1344                                         }, this);
1345                                 }),
1346                                 _onClear: d_1(function () {
1347                                         if (this.__redo__) { clear.call(this.__redo__); }
1348                                         this.__nextIndex__ = 0;
1349                                 })
1350                         })
1351                 )
1352         );
1353
1354         defineProperty$6(
1355                 Iterator.prototype,
1356                 es6Symbol$1.iterator,
1357                 d_1(function () {
1358                         return this;
1359                 })
1360         );
1361
1362         var array = createCommonjsModule(function (module) {
1363
1364
1365
1366         var defineProperty = Object.defineProperty, ArrayIterator;
1367
1368         ArrayIterator = module.exports = function (arr, kind) {
1369                 if (!(this instanceof ArrayIterator)) { throw new TypeError("Constructor requires 'new'"); }
1370                 es6Iterator.call(this, arr);
1371                 if (!kind) { kind = "value"; }
1372                 else if (contains.call(kind, "key+value")) { kind = "key+value"; }
1373                 else if (contains.call(kind, "key")) { kind = "key"; }
1374                 else { kind = "value"; }
1375                 defineProperty(this, "__kind__", d_1("", kind));
1376         };
1377         if (setPrototypeOf) { setPrototypeOf(ArrayIterator, es6Iterator); }
1378
1379         // Internal %ArrayIteratorPrototype% doesn't expose its constructor
1380         delete ArrayIterator.prototype.constructor;
1381
1382         ArrayIterator.prototype = Object.create(es6Iterator.prototype, {
1383                 _resolve: d_1(function (i) {
1384                         if (this.__kind__ === "value") { return this.__list__[i]; }
1385                         if (this.__kind__ === "key+value") { return [i, this.__list__[i]]; }
1386                         return i;
1387                 })
1388         });
1389         defineProperty(ArrayIterator.prototype, es6Symbol$1.toStringTag, d_1("c", "Array Iterator"));
1390         });
1391
1392         var string = createCommonjsModule(function (module) {
1393
1394
1395
1396         var defineProperty = Object.defineProperty, StringIterator;
1397
1398         StringIterator = module.exports = function (str) {
1399                 if (!(this instanceof StringIterator)) { throw new TypeError("Constructor requires 'new'"); }
1400                 str = String(str);
1401                 es6Iterator.call(this, str);
1402                 defineProperty(this, "__length__", d_1("", str.length));
1403         };
1404         if (setPrototypeOf) { setPrototypeOf(StringIterator, es6Iterator); }
1405
1406         // Internal %ArrayIteratorPrototype% doesn't expose its constructor
1407         delete StringIterator.prototype.constructor;
1408
1409         StringIterator.prototype = Object.create(es6Iterator.prototype, {
1410                 _next: d_1(function () {
1411                         if (!this.__list__) { return undefined; }
1412                         if (this.__nextIndex__ < this.__length__) { return this.__nextIndex__++; }
1413                         this._unBind();
1414                         return undefined;
1415                 }),
1416                 _resolve: d_1(function (i) {
1417                         var char = this.__list__[i], code;
1418                         if (this.__nextIndex__ === this.__length__) { return char; }
1419                         code = char.charCodeAt(0);
1420                         if (code >= 0xd800 && code <= 0xdbff) { return char + this.__list__[this.__nextIndex__++]; }
1421                         return char;
1422                 })
1423         });
1424         defineProperty(StringIterator.prototype, es6Symbol$1.toStringTag, d_1("c", "String Iterator"));
1425         });
1426
1427         var iteratorSymbol$2 = es6Symbol$1.iterator;
1428
1429         var get = function (obj) {
1430                 if (typeof validIterable(obj)[iteratorSymbol$2] === "function") { return obj[iteratorSymbol$2](); }
1431                 if (isArguments(obj)) { return new array(obj); }
1432                 if (isString(obj)) { return new string(obj); }
1433                 return new array(obj);
1434         };
1435
1436         var isArray$2 = Array.isArray, call$3 = Function.prototype.call, some = Array.prototype.some;
1437
1438         var forOf = function (iterable, cb /*, thisArg*/) {
1439                 var mode, thisArg = arguments[2], result, doBreak, broken, i, length, char, code;
1440                 if (isArray$2(iterable) || isArguments(iterable)) { mode = "array"; }
1441                 else if (isString(iterable)) { mode = "string"; }
1442                 else { iterable = get(iterable); }
1443
1444                 validCallable(cb);
1445                 doBreak = function () {
1446                         broken = true;
1447                 };
1448                 if (mode === "array") {
1449                         some.call(iterable, function (value) {
1450                                 call$3.call(cb, thisArg, value, doBreak);
1451                                 return broken;
1452                         });
1453                         return;
1454                 }
1455                 if (mode === "string") {
1456                         length = iterable.length;
1457                         for (i = 0; i < length; ++i) {
1458                                 char = iterable[i];
1459                                 if (i + 1 < length) {
1460                                         code = char.charCodeAt(0);
1461                                         if (code >= 0xd800 && code <= 0xdbff) { char += iterable[++i]; }
1462                                 }
1463                                 call$3.call(cb, thisArg, char, doBreak);
1464                                 if (broken) { break; }
1465                         }
1466                         return;
1467                 }
1468                 result = iterable.next();
1469
1470                 while (!result.done) {
1471                         call$3.call(cb, thisArg, result.value, doBreak);
1472                         if (broken) { return; }
1473                         result = iterable.next();
1474                 }
1475         };
1476
1477         var iterator = createCommonjsModule(function (module) {
1478
1479         var toStringTagSymbol = es6Symbol.toStringTag
1480
1481           , defineProperty = Object.defineProperty
1482           , SetIterator;
1483
1484         SetIterator = module.exports = function (set, kind) {
1485                 if (!(this instanceof SetIterator)) { return new SetIterator(set, kind); }
1486                 es6Iterator.call(this, set.__setData__, set);
1487                 if (!kind) { kind = 'value'; }
1488                 else if (contains.call(kind, 'key+value')) { kind = 'key+value'; }
1489                 else { kind = 'value'; }
1490                 defineProperty(this, '__kind__', d_1('', kind));
1491         };
1492         if (setPrototypeOf) { setPrototypeOf(SetIterator, es6Iterator); }
1493
1494         SetIterator.prototype = Object.create(es6Iterator.prototype, {
1495                 constructor: d_1(SetIterator),
1496                 _resolve: d_1(function (i) {
1497                         if (this.__kind__ === 'value') { return this.__list__[i]; }
1498                         return [this.__list__[i], this.__list__[i]];
1499                 }),
1500                 toString: d_1(function () { return '[object Set Iterator]'; })
1501         });
1502         defineProperty(SetIterator.prototype, toStringTagSymbol, d_1('c', 'Set Iterator'));
1503         });
1504
1505         // Exports true if environment provides native `Set` implementation,
1506
1507         var isNativeImplemented = (function () {
1508                 if (typeof Set === 'undefined') { return false; }
1509                 return (Object.prototype.toString.call(Set.prototype) === '[object Set]');
1510         }());
1511
1512         var iterator$1       = validIterable
1513
1514           , call$4 = Function.prototype.call
1515           , defineProperty$7 = Object.defineProperty, getPrototypeOf$1 = Object.getPrototypeOf
1516           , SetPoly, getValues, NativeSet;
1517
1518         if (isNativeImplemented) { NativeSet = Set; }
1519
1520         var polyfill$2 = SetPoly = function Set(/*iterable*/) {
1521                 var iterable = arguments[0], self;
1522                 if (!(this instanceof SetPoly)) { throw new TypeError('Constructor requires \'new\''); }
1523                 if (isNativeImplemented && setPrototypeOf) { self = setPrototypeOf(new NativeSet(), getPrototypeOf$1(this)); }
1524                 else { self = this; }
1525                 if (iterable != null) { iterator$1(iterable); }
1526                 defineProperty$7(self, '__setData__', d_1('c', []));
1527                 if (!iterable) { return self; }
1528                 forOf(iterable, function (value) {
1529                         if (eIndexOf.call(this, value) !== -1) { return; }
1530                         this.push(value);
1531                 }, self.__setData__);
1532                 return self;
1533         };
1534
1535         if (isNativeImplemented) {
1536                 if (setPrototypeOf) { setPrototypeOf(SetPoly, NativeSet); }
1537                 SetPoly.prototype = Object.create(NativeSet.prototype, { constructor: d_1(SetPoly) });
1538         }
1539
1540         eventEmitter(Object.defineProperties(SetPoly.prototype, {
1541                 add: d_1(function (value) {
1542                         if (this.has(value)) { return this; }
1543                         this.emit('_add', this.__setData__.push(value) - 1, value);
1544                         return this;
1545                 }),
1546                 clear: d_1(function () {
1547                         if (!this.__setData__.length) { return; }
1548                         clear.call(this.__setData__);
1549                         this.emit('_clear');
1550                 }),
1551                 delete: d_1(function (value) {
1552                         var index = eIndexOf.call(this.__setData__, value);
1553                         if (index === -1) { return false; }
1554                         this.__setData__.splice(index, 1);
1555                         this.emit('_delete', index, value);
1556                         return true;
1557                 }),
1558                 entries: d_1(function () { return new iterator(this, 'key+value'); }),
1559                 forEach: d_1(function (cb/*, thisArg*/) {
1560                         var thisArg = arguments[1], iterator, result, value;
1561                         validCallable(cb);
1562                         iterator = this.values();
1563                         result = iterator._next();
1564                         while (result !== undefined) {
1565                                 value = iterator._resolve(result);
1566                                 call$4.call(cb, thisArg, value, value, this);
1567                                 result = iterator._next();
1568                         }
1569                 }),
1570                 has: d_1(function (value) {
1571                         return (eIndexOf.call(this.__setData__, value) !== -1);
1572                 }),
1573                 keys: d_1(getValues = function () { return this.values(); }),
1574                 size: d_1.gs(function () { return this.__setData__.length; }),
1575                 values: d_1(function () { return new iterator(this); }),
1576                 toString: d_1(function () { return '[object Set]'; })
1577         }));
1578         defineProperty$7(SetPoly.prototype, es6Symbol.iterator, d_1(getValues));
1579         defineProperty$7(SetPoly.prototype, es6Symbol.toStringTag, d_1('c', 'Set'));
1580
1581         var es6Set = isImplemented() ? Set : polyfill$2;
1582
1583         var isImplemented$b = function () {
1584                 var map, iterator, result;
1585                 if (typeof Map !== 'function') { return false; }
1586                 try {
1587                         // WebKit doesn't support arguments and crashes
1588                         map = new Map([['raz', 'one'], ['dwa', 'two'], ['trzy', 'three']]);
1589                 } catch (e) {
1590                         return false;
1591                 }
1592                 if (String(map) !== '[object Map]') { return false; }
1593                 if (map.size !== 3) { return false; }
1594                 if (typeof map.clear !== 'function') { return false; }
1595                 if (typeof map.delete !== 'function') { return false; }
1596                 if (typeof map.entries !== 'function') { return false; }
1597                 if (typeof map.forEach !== 'function') { return false; }
1598                 if (typeof map.get !== 'function') { return false; }
1599                 if (typeof map.has !== 'function') { return false; }
1600                 if (typeof map.keys !== 'function') { return false; }
1601                 if (typeof map.set !== 'function') { return false; }
1602                 if (typeof map.values !== 'function') { return false; }
1603
1604                 iterator = map.entries();
1605                 result = iterator.next();
1606                 if (result.done !== false) { return false; }
1607                 if (!result.value) { return false; }
1608                 if (result.value[0] !== 'raz') { return false; }
1609                 if (result.value[1] !== 'one') { return false; }
1610
1611                 return true;
1612         };
1613
1614         var forEach$2 = Array.prototype.forEach, create$6 = Object.create;
1615
1616         // eslint-disable-next-line no-unused-vars
1617         var primitiveSet = function (arg/*, …args*/) {
1618                 var set = create$6(null);
1619                 forEach$2.call(arguments, function (name) { set[name] = true; });
1620                 return set;
1621         };
1622
1623         var iteratorKinds = primitiveSet('key',
1624                 'value', 'key+value');
1625
1626         var iterator$2 = createCommonjsModule(function (module) {
1627
1628         var toStringTagSymbol = es6Symbol$1.toStringTag
1629
1630           , defineProperties = Object.defineProperties
1631           , unBind = es6Iterator.prototype._unBind
1632           , MapIterator;
1633
1634         MapIterator = module.exports = function (map, kind) {
1635                 if (!(this instanceof MapIterator)) { return new MapIterator(map, kind); }
1636                 es6Iterator.call(this, map.__mapKeysData__, map);
1637                 if (!kind || !iteratorKinds[kind]) { kind = 'key+value'; }
1638                 defineProperties(this, {
1639                         __kind__: d_1('', kind),
1640                         __values__: d_1('w', map.__mapValuesData__)
1641                 });
1642         };
1643         if (setPrototypeOf) { setPrototypeOf(MapIterator, es6Iterator); }
1644
1645         MapIterator.prototype = Object.create(es6Iterator.prototype, {
1646                 constructor: d_1(MapIterator),
1647                 _resolve: d_1(function (i) {
1648                         if (this.__kind__ === 'value') { return this.__values__[i]; }
1649                         if (this.__kind__ === 'key') { return this.__list__[i]; }
1650                         return [this.__list__[i], this.__values__[i]];
1651                 }),
1652                 _unBind: d_1(function () {
1653                         this.__values__ = null;
1654                         unBind.call(this);
1655                 }),
1656                 toString: d_1(function () { return '[object Map Iterator]'; })
1657         });
1658         Object.defineProperty(MapIterator.prototype, toStringTagSymbol,
1659                 d_1('c', 'Map Iterator'));
1660         });
1661
1662         // Exports true if environment provides native `Map` implementation,
1663
1664         var isNativeImplemented$1 = (function () {
1665                 if (typeof Map === 'undefined') { return false; }
1666                 return (Object.prototype.toString.call(new Map()) === '[object Map]');
1667         }());
1668
1669         var iterator$3       = validIterable
1670
1671           , call$5 = Function.prototype.call
1672           , defineProperties$3 = Object.defineProperties, getPrototypeOf$2 = Object.getPrototypeOf
1673           , MapPoly;
1674
1675         var polyfill$3 = MapPoly = function (/*iterable*/) {
1676                 var iterable = arguments[0], keys, values, self;
1677                 if (!(this instanceof MapPoly)) { throw new TypeError('Constructor requires \'new\''); }
1678                 if (isNativeImplemented$1 && setPrototypeOf && (Map !== MapPoly)) {
1679                         self = setPrototypeOf(new Map(), getPrototypeOf$2(this));
1680                 } else {
1681                         self = this;
1682                 }
1683                 if (iterable != null) { iterator$3(iterable); }
1684                 defineProperties$3(self, {
1685                         __mapKeysData__: d_1('c', keys = []),
1686                         __mapValuesData__: d_1('c', values = [])
1687                 });
1688                 if (!iterable) { return self; }
1689                 forOf(iterable, function (value) {
1690                         var key = validValue(value)[0];
1691                         value = value[1];
1692                         if (eIndexOf.call(keys, key) !== -1) { return; }
1693                         keys.push(key);
1694                         values.push(value);
1695                 }, self);
1696                 return self;
1697         };
1698
1699         if (isNativeImplemented$1) {
1700                 if (setPrototypeOf) { setPrototypeOf(MapPoly, Map); }
1701                 MapPoly.prototype = Object.create(Map.prototype, {
1702                         constructor: d_1(MapPoly)
1703                 });
1704         }
1705
1706         eventEmitter(defineProperties$3(MapPoly.prototype, {
1707                 clear: d_1(function () {
1708                         if (!this.__mapKeysData__.length) { return; }
1709                         clear.call(this.__mapKeysData__);
1710                         clear.call(this.__mapValuesData__);
1711                         this.emit('_clear');
1712                 }),
1713                 delete: d_1(function (key) {
1714                         var index = eIndexOf.call(this.__mapKeysData__, key);
1715                         if (index === -1) { return false; }
1716                         this.__mapKeysData__.splice(index, 1);
1717                         this.__mapValuesData__.splice(index, 1);
1718                         this.emit('_delete', index, key);
1719                         return true;
1720                 }),
1721                 entries: d_1(function () { return new iterator$2(this, 'key+value'); }),
1722                 forEach: d_1(function (cb/*, thisArg*/) {
1723                         var thisArg = arguments[1], iterator, result;
1724                         validCallable(cb);
1725                         iterator = this.entries();
1726                         result = iterator._next();
1727                         while (result !== undefined) {
1728                                 call$5.call(cb, thisArg, this.__mapValuesData__[result],
1729                                         this.__mapKeysData__[result], this);
1730                                 result = iterator._next();
1731                         }
1732                 }),
1733                 get: d_1(function (key) {
1734                         var index = eIndexOf.call(this.__mapKeysData__, key);
1735                         if (index === -1) { return; }
1736                         return this.__mapValuesData__[index];
1737                 }),
1738                 has: d_1(function (key) {
1739                         return (eIndexOf.call(this.__mapKeysData__, key) !== -1);
1740                 }),
1741                 keys: d_1(function () { return new iterator$2(this, 'key'); }),
1742                 set: d_1(function (key, value) {
1743                         var index = eIndexOf.call(this.__mapKeysData__, key), emit;
1744                         if (index === -1) {
1745                                 index = this.__mapKeysData__.push(key) - 1;
1746                                 emit = true;
1747                         }
1748                         this.__mapValuesData__[index] = value;
1749                         if (emit) { this.emit('_add', index, key); }
1750                         return this;
1751                 }),
1752                 size: d_1.gs(function () { return this.__mapKeysData__.length; }),
1753                 values: d_1(function () { return new iterator$2(this, 'value'); }),
1754                 toString: d_1(function () { return '[object Map]'; })
1755         }));
1756         Object.defineProperty(MapPoly.prototype, es6Symbol$1.iterator, d_1(function () {
1757                 return this.entries();
1758         }));
1759         Object.defineProperty(MapPoly.prototype, es6Symbol$1.toStringTag, d_1('c', 'Map'));
1760
1761         var es6Map = isImplemented$b() ? Map : polyfill$3;
1762
1763         var toStr = Object.prototype.toString;
1764
1765         var isArguments$1 = function isArguments(value) {
1766                 var str = toStr.call(value);
1767                 var isArgs = str === '[object Arguments]';
1768                 if (!isArgs) {
1769                         isArgs = str !== '[object Array]' &&
1770                                 value !== null &&
1771                                 typeof value === 'object' &&
1772                                 typeof value.length === 'number' &&
1773                                 value.length >= 0 &&
1774                                 toStr.call(value.callee) === '[object Function]';
1775                 }
1776                 return isArgs;
1777         };
1778
1779         var keysShim;
1780         if (!Object.keys) {
1781                 // modified from https://github.com/es-shims/es5-shim
1782                 var has = Object.prototype.hasOwnProperty;
1783                 var toStr$1 = Object.prototype.toString;
1784                 var isArgs = isArguments$1; // eslint-disable-line global-require
1785                 var isEnumerable = Object.prototype.propertyIsEnumerable;
1786                 var hasDontEnumBug = !isEnumerable.call({ toString: null }, 'toString');
1787                 var hasProtoEnumBug = isEnumerable.call(function () {}, 'prototype');
1788                 var dontEnums = [
1789                         'toString',
1790                         'toLocaleString',
1791                         'valueOf',
1792                         'hasOwnProperty',
1793                         'isPrototypeOf',
1794                         'propertyIsEnumerable',
1795                         'constructor'
1796                 ];
1797                 var equalsConstructorPrototype = function (o) {
1798                         var ctor = o.constructor;
1799                         return ctor && ctor.prototype === o;
1800                 };
1801                 var excludedKeys = {
1802                         $applicationCache: true,
1803                         $console: true,
1804                         $external: true,
1805                         $frame: true,
1806                         $frameElement: true,
1807                         $frames: true,
1808                         $innerHeight: true,
1809                         $innerWidth: true,
1810                         $onmozfullscreenchange: true,
1811                         $onmozfullscreenerror: true,
1812                         $outerHeight: true,
1813                         $outerWidth: true,
1814                         $pageXOffset: true,
1815                         $pageYOffset: true,
1816                         $parent: true,
1817                         $scrollLeft: true,
1818                         $scrollTop: true,
1819                         $scrollX: true,
1820                         $scrollY: true,
1821                         $self: true,
1822                         $webkitIndexedDB: true,
1823                         $webkitStorageInfo: true,
1824                         $window: true
1825                 };
1826                 var hasAutomationEqualityBug = (function () {
1827                         /* global window */
1828                         if (typeof window === 'undefined') { return false; }
1829                         for (var k in window) {
1830                                 try {
1831                                         if (!excludedKeys['$' + k] && has.call(window, k) && window[k] !== null && typeof window[k] === 'object') {
1832                                                 try {
1833                                                         equalsConstructorPrototype(window[k]);
1834                                                 } catch (e) {
1835                                                         return true;
1836                                                 }
1837                                         }
1838                                 } catch (e$1) {
1839                                         return true;
1840                                 }
1841                         }
1842                         return false;
1843                 }());
1844                 var equalsConstructorPrototypeIfNotBuggy = function (o) {
1845                         /* global window */
1846                         if (typeof window === 'undefined' || !hasAutomationEqualityBug) {
1847                                 return equalsConstructorPrototype(o);
1848                         }
1849                         try {
1850                                 return equalsConstructorPrototype(o);
1851                         } catch (e) {
1852                                 return false;
1853                         }
1854                 };
1855
1856                 keysShim = function keys(object) {
1857                         var isObject = object !== null && typeof object === 'object';
1858                         var isFunction = toStr$1.call(object) === '[object Function]';
1859                         var isArguments = isArgs(object);
1860                         var isString = isObject && toStr$1.call(object) === '[object String]';
1861                         var theKeys = [];
1862
1863                         if (!isObject && !isFunction && !isArguments) {
1864                                 throw new TypeError('Object.keys called on a non-object');
1865                         }
1866
1867                         var skipProto = hasProtoEnumBug && isFunction;
1868                         if (isString && object.length > 0 && !has.call(object, 0)) {
1869                                 for (var i = 0; i < object.length; ++i) {
1870                                         theKeys.push(String(i));
1871                                 }
1872                         }
1873
1874                         if (isArguments && object.length > 0) {
1875                                 for (var j = 0; j < object.length; ++j) {
1876                                         theKeys.push(String(j));
1877                                 }
1878                         } else {
1879                                 for (var name in object) {
1880                                         if (!(skipProto && name === 'prototype') && has.call(object, name)) {
1881                                                 theKeys.push(String(name));
1882                                         }
1883                                 }
1884                         }
1885
1886                         if (hasDontEnumBug) {
1887                                 var skipConstructor = equalsConstructorPrototypeIfNotBuggy(object);
1888
1889                                 for (var k = 0; k < dontEnums.length; ++k) {
1890                                         if (!(skipConstructor && dontEnums[k] === 'constructor') && has.call(object, dontEnums[k])) {
1891                                                 theKeys.push(dontEnums[k]);
1892                                         }
1893                                 }
1894                         }
1895                         return theKeys;
1896                 };
1897         }
1898         var implementation$1 = keysShim;
1899
1900         var slice = Array.prototype.slice;
1901
1902
1903         var origKeys = Object.keys;
1904         var keysShim$1 = origKeys ? function keys(o) { return origKeys(o); } : implementation$1;
1905
1906         var originalKeys = Object.keys;
1907
1908         keysShim$1.shim = function shimObjectKeys() {
1909                 if (Object.keys) {
1910                         var keysWorksWithArguments = (function () {
1911                                 // Safari 5.0 bug
1912                                 var args = Object.keys(arguments);
1913                                 return args && args.length === arguments.length;
1914                         }(1, 2));
1915                         if (!keysWorksWithArguments) {
1916                                 Object.keys = function keys(object) { // eslint-disable-line func-name-matching
1917                                         if (isArguments$1(object)) {
1918                                                 return originalKeys(slice.call(object));
1919                                         }
1920                                         return originalKeys(object);
1921                                 };
1922                         }
1923                 } else {
1924                         Object.keys = keysShim$1;
1925                 }
1926                 return Object.keys || keysShim$1;
1927         };
1928
1929         var objectKeys = keysShim$1;
1930
1931         var hasSymbols = typeof Symbol === 'function' && typeof Symbol('foo') === 'symbol';
1932
1933         var toStr$2 = Object.prototype.toString;
1934         var concat = Array.prototype.concat;
1935         var origDefineProperty = Object.defineProperty;
1936
1937         var isFunction$1 = function (fn) {
1938                 return typeof fn === 'function' && toStr$2.call(fn) === '[object Function]';
1939         };
1940
1941         var arePropertyDescriptorsSupported = function () {
1942                 var obj = {};
1943                 try {
1944                         origDefineProperty(obj, 'x', { enumerable: false, value: obj });
1945                         // eslint-disable-next-line no-unused-vars, no-restricted-syntax
1946                         for (var _ in obj) { // jscs:ignore disallowUnusedVariables
1947                                 return false;
1948                         }
1949                         return obj.x === obj;
1950                 } catch (e) { /* this is IE 8. */
1951                         return false;
1952                 }
1953         };
1954         var supportsDescriptors = origDefineProperty && arePropertyDescriptorsSupported();
1955
1956         var defineProperty$8 = function (object, name, value, predicate) {
1957                 if (name in object && (!isFunction$1(predicate) || !predicate())) {
1958                         return;
1959                 }
1960                 if (supportsDescriptors) {
1961                         origDefineProperty(object, name, {
1962                                 configurable: true,
1963                                 enumerable: false,
1964                                 value: value,
1965                                 writable: true
1966                         });
1967                 } else {
1968                         object[name] = value;
1969                 }
1970         };
1971
1972         var defineProperties$4 = function (object, map) {
1973                 var predicates = arguments.length > 2 ? arguments[2] : {};
1974                 var props = objectKeys(map);
1975                 if (hasSymbols) {
1976                         props = concat.call(props, Object.getOwnPropertySymbols(map));
1977                 }
1978                 for (var i = 0; i < props.length; i += 1) {
1979                         defineProperty$8(object, props[i], map[props[i]], predicates[props[i]]);
1980                 }
1981         };
1982
1983         defineProperties$4.supportsDescriptors = !!supportsDescriptors;
1984
1985         var defineProperties_1 = defineProperties$4;
1986
1987         /* eslint complexity: [2, 18], max-statements: [2, 33] */
1988         var shams = function hasSymbols() {
1989                 if (typeof Symbol !== 'function' || typeof Object.getOwnPropertySymbols !== 'function') { return false; }
1990                 if (typeof Symbol.iterator === 'symbol') { return true; }
1991
1992                 var obj = {};
1993                 var sym = Symbol('test');
1994                 var symObj = Object(sym);
1995                 if (typeof sym === 'string') { return false; }
1996
1997                 if (Object.prototype.toString.call(sym) !== '[object Symbol]') { return false; }
1998                 if (Object.prototype.toString.call(symObj) !== '[object Symbol]') { return false; }
1999
2000                 // temp disabled per https://github.com/ljharb/object.assign/issues/17
2001                 // if (sym instanceof Symbol) { return false; }
2002                 // temp disabled per https://github.com/WebReflection/get-own-property-symbols/issues/4
2003                 // if (!(symObj instanceof Symbol)) { return false; }
2004
2005                 // if (typeof Symbol.prototype.toString !== 'function') { return false; }
2006                 // if (String(sym) !== Symbol.prototype.toString.call(sym)) { return false; }
2007
2008                 var symVal = 42;
2009                 obj[sym] = symVal;
2010                 for (sym in obj) { return false; } // eslint-disable-line no-restricted-syntax
2011                 if (typeof Object.keys === 'function' && Object.keys(obj).length !== 0) { return false; }
2012
2013                 if (typeof Object.getOwnPropertyNames === 'function' && Object.getOwnPropertyNames(obj).length !== 0) { return false; }
2014
2015                 var syms = Object.getOwnPropertySymbols(obj);
2016                 if (syms.length !== 1 || syms[0] !== sym) { return false; }
2017
2018                 if (!Object.prototype.propertyIsEnumerable.call(obj, sym)) { return false; }
2019
2020                 if (typeof Object.getOwnPropertyDescriptor === 'function') {
2021                         var descriptor = Object.getOwnPropertyDescriptor(obj, sym);
2022                         if (descriptor.value !== symVal || descriptor.enumerable !== true) { return false; }
2023                 }
2024
2025                 return true;
2026         };
2027
2028         var origSymbol = commonjsGlobal.Symbol;
2029
2030
2031         var hasSymbols$1 = function hasNativeSymbols() {
2032                 if (typeof origSymbol !== 'function') { return false; }
2033                 if (typeof Symbol !== 'function') { return false; }
2034                 if (typeof origSymbol('foo') !== 'symbol') { return false; }
2035                 if (typeof Symbol('bar') !== 'symbol') { return false; }
2036
2037                 return shams();
2038         };
2039
2040         /* eslint no-invalid-this: 1 */
2041
2042         var ERROR_MESSAGE = 'Function.prototype.bind called on incompatible ';
2043         var slice$1 = Array.prototype.slice;
2044         var toStr$3 = Object.prototype.toString;
2045         var funcType = '[object Function]';
2046
2047         var implementation$2 = function bind(that) {
2048             var target = this;
2049             if (typeof target !== 'function' || toStr$3.call(target) !== funcType) {
2050                 throw new TypeError(ERROR_MESSAGE + target);
2051             }
2052             var args = slice$1.call(arguments, 1);
2053
2054             var bound;
2055             var binder = function () {
2056                 if (this instanceof bound) {
2057                     var result = target.apply(
2058                         this,
2059                         args.concat(slice$1.call(arguments))
2060                     );
2061                     if (Object(result) === result) {
2062                         return result;
2063                     }
2064                     return this;
2065                 } else {
2066                     return target.apply(
2067                         that,
2068                         args.concat(slice$1.call(arguments))
2069                     );
2070                 }
2071             };
2072
2073             var boundLength = Math.max(0, target.length - args.length);
2074             var boundArgs = [];
2075             for (var i = 0; i < boundLength; i++) {
2076                 boundArgs.push('$' + i);
2077             }
2078
2079             bound = Function('binder', 'return function (' + boundArgs.join(',') + '){ return binder.apply(this,arguments); }')(binder);
2080
2081             if (target.prototype) {
2082                 var Empty = function Empty() {};
2083                 Empty.prototype = target.prototype;
2084                 bound.prototype = new Empty();
2085                 Empty.prototype = null;
2086             }
2087
2088             return bound;
2089         };
2090
2091         var functionBind = Function.prototype.bind || implementation$2;
2092
2093         /* globals
2094                 Atomics,
2095                 SharedArrayBuffer,
2096         */
2097
2098         var undefined$1;
2099
2100         var $TypeError = TypeError;
2101
2102         var $gOPD = Object.getOwnPropertyDescriptor;
2103         if ($gOPD) {
2104                 try {
2105                         $gOPD({}, '');
2106                 } catch (e) {
2107                         $gOPD = null; // this is IE 8, which has a broken gOPD
2108                 }
2109         }
2110
2111         var throwTypeError = function () { throw new $TypeError(); };
2112         var ThrowTypeError = $gOPD
2113                 ? (function () {
2114                         try {
2115                                 // eslint-disable-next-line no-unused-expressions, no-caller, no-restricted-properties
2116                                 arguments.callee; // IE 8 does not throw here
2117                                 return throwTypeError;
2118                         } catch (calleeThrows) {
2119                                 try {
2120                                         // IE 8 throws on Object.getOwnPropertyDescriptor(arguments, '')
2121                                         return $gOPD(arguments, 'callee').get;
2122                                 } catch (gOPDthrows) {
2123                                         return throwTypeError;
2124                                 }
2125                         }
2126                 }())
2127                 : throwTypeError;
2128
2129         var hasSymbols$2 = hasSymbols$1();
2130
2131         var getProto = Object.getPrototypeOf || function (x) { return x.__proto__; }; // eslint-disable-line no-proto
2132         var generatorFunction =  undefined$1;
2133         var asyncFunction =  undefined$1;
2134         var asyncGenFunction =  undefined$1;
2135
2136         var TypedArray = typeof Uint8Array === 'undefined' ? undefined$1 : getProto(Uint8Array);
2137
2138         var INTRINSICS = {
2139                 '%Array%': Array,
2140                 '%ArrayBuffer%': typeof ArrayBuffer === 'undefined' ? undefined$1 : ArrayBuffer,
2141                 '%ArrayBufferPrototype%': typeof ArrayBuffer === 'undefined' ? undefined$1 : ArrayBuffer.prototype,
2142                 '%ArrayIteratorPrototype%': hasSymbols$2 ? getProto([][Symbol.iterator]()) : undefined$1,
2143                 '%ArrayPrototype%': Array.prototype,
2144                 '%ArrayProto_entries%': Array.prototype.entries,
2145                 '%ArrayProto_forEach%': Array.prototype.forEach,
2146                 '%ArrayProto_keys%': Array.prototype.keys,
2147                 '%ArrayProto_values%': Array.prototype.values,
2148                 '%AsyncFromSyncIteratorPrototype%': undefined$1,
2149                 '%AsyncFunction%': asyncFunction,
2150                 '%AsyncFunctionPrototype%':  undefined$1,
2151                 '%AsyncGenerator%':  undefined$1,
2152                 '%AsyncGeneratorFunction%': asyncGenFunction,
2153                 '%AsyncGeneratorPrototype%':  undefined$1,
2154                 '%AsyncIteratorPrototype%':  undefined$1,
2155                 '%Atomics%': typeof Atomics === 'undefined' ? undefined$1 : Atomics,
2156                 '%Boolean%': Boolean,
2157                 '%BooleanPrototype%': Boolean.prototype,
2158                 '%DataView%': typeof DataView === 'undefined' ? undefined$1 : DataView,
2159                 '%DataViewPrototype%': typeof DataView === 'undefined' ? undefined$1 : DataView.prototype,
2160                 '%Date%': Date,
2161                 '%DatePrototype%': Date.prototype,
2162                 '%decodeURI%': decodeURI,
2163                 '%decodeURIComponent%': decodeURIComponent,
2164                 '%encodeURI%': encodeURI,
2165                 '%encodeURIComponent%': encodeURIComponent,
2166                 '%Error%': Error,
2167                 '%ErrorPrototype%': Error.prototype,
2168                 '%eval%': eval, // eslint-disable-line no-eval
2169                 '%EvalError%': EvalError,
2170                 '%EvalErrorPrototype%': EvalError.prototype,
2171                 '%Float32Array%': typeof Float32Array === 'undefined' ? undefined$1 : Float32Array,
2172                 '%Float32ArrayPrototype%': typeof Float32Array === 'undefined' ? undefined$1 : Float32Array.prototype,
2173                 '%Float64Array%': typeof Float64Array === 'undefined' ? undefined$1 : Float64Array,
2174                 '%Float64ArrayPrototype%': typeof Float64Array === 'undefined' ? undefined$1 : Float64Array.prototype,
2175                 '%Function%': Function,
2176                 '%FunctionPrototype%': Function.prototype,
2177                 '%Generator%':  undefined$1,
2178                 '%GeneratorFunction%': generatorFunction,
2179                 '%GeneratorPrototype%':  undefined$1,
2180                 '%Int8Array%': typeof Int8Array === 'undefined' ? undefined$1 : Int8Array,
2181                 '%Int8ArrayPrototype%': typeof Int8Array === 'undefined' ? undefined$1 : Int8Array.prototype,
2182                 '%Int16Array%': typeof Int16Array === 'undefined' ? undefined$1 : Int16Array,
2183                 '%Int16ArrayPrototype%': typeof Int16Array === 'undefined' ? undefined$1 : Int8Array.prototype,
2184                 '%Int32Array%': typeof Int32Array === 'undefined' ? undefined$1 : Int32Array,
2185                 '%Int32ArrayPrototype%': typeof Int32Array === 'undefined' ? undefined$1 : Int32Array.prototype,
2186                 '%isFinite%': isFinite,
2187                 '%isNaN%': isNaN,
2188                 '%IteratorPrototype%': hasSymbols$2 ? getProto(getProto([][Symbol.iterator]())) : undefined$1,
2189                 '%JSON%': typeof JSON === 'object' ? JSON : undefined$1,
2190                 '%JSONParse%': typeof JSON === 'object' ? JSON.parse : undefined$1,
2191                 '%Map%': typeof Map === 'undefined' ? undefined$1 : Map,
2192                 '%MapIteratorPrototype%': typeof Map === 'undefined' || !hasSymbols$2 ? undefined$1 : getProto(new Map()[Symbol.iterator]()),
2193                 '%MapPrototype%': typeof Map === 'undefined' ? undefined$1 : Map.prototype,
2194                 '%Math%': Math,
2195                 '%Number%': Number,
2196                 '%NumberPrototype%': Number.prototype,
2197                 '%Object%': Object,
2198                 '%ObjectPrototype%': Object.prototype,
2199                 '%ObjProto_toString%': Object.prototype.toString,
2200                 '%ObjProto_valueOf%': Object.prototype.valueOf,
2201                 '%parseFloat%': parseFloat,
2202                 '%parseInt%': parseInt,
2203                 '%Promise%': typeof Promise === 'undefined' ? undefined$1 : Promise,
2204                 '%PromisePrototype%': typeof Promise === 'undefined' ? undefined$1 : Promise.prototype,
2205                 '%PromiseProto_then%': typeof Promise === 'undefined' ? undefined$1 : Promise.prototype.then,
2206                 '%Promise_all%': typeof Promise === 'undefined' ? undefined$1 : Promise.all,
2207                 '%Promise_reject%': typeof Promise === 'undefined' ? undefined$1 : Promise.reject,
2208                 '%Promise_resolve%': typeof Promise === 'undefined' ? undefined$1 : Promise.resolve,
2209                 '%Proxy%': typeof Proxy === 'undefined' ? undefined$1 : Proxy,
2210                 '%RangeError%': RangeError,
2211                 '%RangeErrorPrototype%': RangeError.prototype,
2212                 '%ReferenceError%': ReferenceError,
2213                 '%ReferenceErrorPrototype%': ReferenceError.prototype,
2214                 '%Reflect%': typeof Reflect === 'undefined' ? undefined$1 : Reflect,
2215                 '%RegExp%': RegExp,
2216                 '%RegExpPrototype%': RegExp.prototype,
2217                 '%Set%': typeof Set === 'undefined' ? undefined$1 : Set,
2218                 '%SetIteratorPrototype%': typeof Set === 'undefined' || !hasSymbols$2 ? undefined$1 : getProto(new Set()[Symbol.iterator]()),
2219                 '%SetPrototype%': typeof Set === 'undefined' ? undefined$1 : Set.prototype,
2220                 '%SharedArrayBuffer%': typeof SharedArrayBuffer === 'undefined' ? undefined$1 : SharedArrayBuffer,
2221                 '%SharedArrayBufferPrototype%': typeof SharedArrayBuffer === 'undefined' ? undefined$1 : SharedArrayBuffer.prototype,
2222                 '%String%': String,
2223                 '%StringIteratorPrototype%': hasSymbols$2 ? getProto(''[Symbol.iterator]()) : undefined$1,
2224                 '%StringPrototype%': String.prototype,
2225                 '%Symbol%': hasSymbols$2 ? Symbol : undefined$1,
2226                 '%SymbolPrototype%': hasSymbols$2 ? Symbol.prototype : undefined$1,
2227                 '%SyntaxError%': SyntaxError,
2228                 '%SyntaxErrorPrototype%': SyntaxError.prototype,
2229                 '%ThrowTypeError%': ThrowTypeError,
2230                 '%TypedArray%': TypedArray,
2231                 '%TypedArrayPrototype%': TypedArray ? TypedArray.prototype : undefined$1,
2232                 '%TypeError%': $TypeError,
2233                 '%TypeErrorPrototype%': $TypeError.prototype,
2234                 '%Uint8Array%': typeof Uint8Array === 'undefined' ? undefined$1 : Uint8Array,
2235                 '%Uint8ArrayPrototype%': typeof Uint8Array === 'undefined' ? undefined$1 : Uint8Array.prototype,
2236                 '%Uint8ClampedArray%': typeof Uint8ClampedArray === 'undefined' ? undefined$1 : Uint8ClampedArray,
2237                 '%Uint8ClampedArrayPrototype%': typeof Uint8ClampedArray === 'undefined' ? undefined$1 : Uint8ClampedArray.prototype,
2238                 '%Uint16Array%': typeof Uint16Array === 'undefined' ? undefined$1 : Uint16Array,
2239                 '%Uint16ArrayPrototype%': typeof Uint16Array === 'undefined' ? undefined$1 : Uint16Array.prototype,
2240                 '%Uint32Array%': typeof Uint32Array === 'undefined' ? undefined$1 : Uint32Array,
2241                 '%Uint32ArrayPrototype%': typeof Uint32Array === 'undefined' ? undefined$1 : Uint32Array.prototype,
2242                 '%URIError%': URIError,
2243                 '%URIErrorPrototype%': URIError.prototype,
2244                 '%WeakMap%': typeof WeakMap === 'undefined' ? undefined$1 : WeakMap,
2245                 '%WeakMapPrototype%': typeof WeakMap === 'undefined' ? undefined$1 : WeakMap.prototype,
2246                 '%WeakSet%': typeof WeakSet === 'undefined' ? undefined$1 : WeakSet,
2247                 '%WeakSetPrototype%': typeof WeakSet === 'undefined' ? undefined$1 : WeakSet.prototype
2248         };
2249
2250
2251         var $replace = functionBind.call(Function.call, String.prototype.replace);
2252
2253         /* adapted from https://github.com/lodash/lodash/blob/4.17.15/dist/lodash.js#L6735-L6744 */
2254         var rePropName = /[^%.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|%$))/g;
2255         var reEscapeChar = /\\(\\)?/g; /** Used to match backslashes in property paths. */
2256         var stringToPath = function stringToPath(string) {
2257                 var result = [];
2258                 $replace(string, rePropName, function (match, number, quote, subString) {
2259                         result[result.length] = quote ? $replace(subString, reEscapeChar, '$1') : (number || match);
2260                 });
2261                 return result;
2262         };
2263         /* end adaptation */
2264
2265         var getBaseIntrinsic = function getBaseIntrinsic(name, allowMissing) {
2266                 if (!(name in INTRINSICS)) {
2267                         throw new SyntaxError('intrinsic ' + name + ' does not exist!');
2268                 }
2269
2270                 // istanbul ignore if // hopefully this is impossible to test :-)
2271                 if (typeof INTRINSICS[name] === 'undefined' && !allowMissing) {
2272                         throw new $TypeError('intrinsic ' + name + ' exists, but is not available. Please file an issue!');
2273                 }
2274
2275                 return INTRINSICS[name];
2276         };
2277
2278         var GetIntrinsic = function GetIntrinsic(name, allowMissing) {
2279                 if (typeof name !== 'string' || name.length === 0) {
2280                         throw new TypeError('intrinsic name must be a non-empty string');
2281                 }
2282                 if (arguments.length > 1 && typeof allowMissing !== 'boolean') {
2283                         throw new TypeError('"allowMissing" argument must be a boolean');
2284                 }
2285
2286                 var parts = stringToPath(name);
2287
2288                 var value = getBaseIntrinsic('%' + (parts.length > 0 ? parts[0] : '') + '%', allowMissing);
2289                 for (var i = 1; i < parts.length; i += 1) {
2290                         if (value != null) {
2291                                 if ($gOPD && (i + 1) >= parts.length) {
2292                                         var desc = $gOPD(value, parts[i]);
2293                                         if (!allowMissing && !(parts[i] in value)) {
2294                                                 throw new $TypeError('base intrinsic for ' + name + ' exists, but the property is not available.');
2295                                         }
2296                                         value = desc ? (desc.get || desc.value) : value[parts[i]];
2297                                 } else {
2298                                         value = value[parts[i]];
2299                                 }
2300                         }
2301                 }
2302                 return value;
2303         };
2304
2305         var $TypeError$1 = GetIntrinsic('%TypeError%');
2306
2307         // http://www.ecma-international.org/ecma-262/5.1/#sec-9.10
2308
2309         var CheckObjectCoercible = function CheckObjectCoercible(value, optMessage) {
2310                 if (value == null) {
2311                         throw new $TypeError$1(optMessage || ('Cannot call method on ' + value));
2312                 }
2313                 return value;
2314         };
2315
2316         var RequireObjectCoercible = CheckObjectCoercible;
2317
2318         var $Object = GetIntrinsic('%Object%');
2319
2320
2321
2322         // https://www.ecma-international.org/ecma-262/6.0/#sec-toobject
2323
2324         var ToObject = function ToObject(value) {
2325                 RequireObjectCoercible(value);
2326                 return $Object(value);
2327         };
2328
2329         var $Math = GetIntrinsic('%Math%');
2330         var $Number = GetIntrinsic('%Number%');
2331
2332         var maxSafeInteger = $Number.MAX_SAFE_INTEGER || $Math.pow(2, 53) - 1;
2333
2334         // http://www.ecma-international.org/ecma-262/5.1/#sec-9.3
2335
2336         var ToNumber = function ToNumber(value) {
2337                 return +value; // eslint-disable-line no-implicit-coercion
2338         };
2339
2340         var _isNaN = Number.isNaN || function isNaN(a) {
2341                 return a !== a;
2342         };
2343
2344         var $isNaN = Number.isNaN || function (a) { return a !== a; };
2345
2346         var _isFinite = Number.isFinite || function (x) { return typeof x === 'number' && !$isNaN(x) && x !== Infinity && x !== -Infinity; };
2347
2348         var sign$1 = function sign(number) {
2349                 return number >= 0 ? 1 : -1;
2350         };
2351
2352         var $Math$1 = GetIntrinsic('%Math%');
2353
2354
2355
2356
2357
2358
2359         var $floor = $Math$1.floor;
2360         var $abs = $Math$1.abs;
2361
2362         // http://www.ecma-international.org/ecma-262/5.1/#sec-9.4
2363
2364         var ToInteger = function ToInteger(value) {
2365                 var number = ToNumber(value);
2366                 if (_isNaN(number)) { return 0; }
2367                 if (number === 0 || !_isFinite(number)) { return number; }
2368                 return sign$1(number) * $floor($abs(number));
2369         };
2370
2371         var $apply = GetIntrinsic('%Function.prototype.apply%');
2372         var $call = GetIntrinsic('%Function.prototype.call%');
2373         var $reflectApply = GetIntrinsic('%Reflect.apply%', true) || functionBind.call($call, $apply);
2374
2375         var callBind = function callBind() {
2376                 return $reflectApply(functionBind, $call, arguments);
2377         };
2378
2379         var apply = function applyBind() {
2380                 return $reflectApply(functionBind, $apply, arguments);
2381         };
2382         callBind.apply = apply;
2383
2384         var $indexOf = callBind(GetIntrinsic('String.prototype.indexOf'));
2385
2386         var callBound = function callBoundIntrinsic(name, allowMissing) {
2387                 var intrinsic = GetIntrinsic(name, !!allowMissing);
2388                 if (typeof intrinsic === 'function' && $indexOf(name, '.prototype.')) {
2389                         return callBind(intrinsic);
2390                 }
2391                 return intrinsic;
2392         };
2393
2394         var $test = GetIntrinsic('RegExp.prototype.test');
2395
2396
2397
2398         var regexTester = function regexTester(regex) {
2399                 return callBind($test, regex);
2400         };
2401
2402         var isPrimitive = function isPrimitive(value) {
2403                 return value === null || (typeof value !== 'function' && typeof value !== 'object');
2404         };
2405
2406         var isPrimitive$1 = function isPrimitive(value) {
2407                 return value === null || (typeof value !== 'function' && typeof value !== 'object');
2408         };
2409
2410         var fnToStr = Function.prototype.toString;
2411         var reflectApply = typeof Reflect === 'object' && Reflect !== null && Reflect.apply;
2412         var badArrayLike;
2413         var isCallableMarker;
2414         if (typeof reflectApply === 'function' && typeof Object.defineProperty === 'function') {
2415                 try {
2416                         badArrayLike = Object.defineProperty({}, 'length', {
2417                                 get: function () {
2418                                         throw isCallableMarker;
2419                                 }
2420                         });
2421                         isCallableMarker = {};
2422                 } catch (_) {
2423                         reflectApply = null;
2424                 }
2425         } else {
2426                 reflectApply = null;
2427         }
2428
2429         var constructorRegex = /^\s*class\b/;
2430         var isES6ClassFn = function isES6ClassFunction(value) {
2431                 try {
2432                         var fnStr = fnToStr.call(value);
2433                         return constructorRegex.test(fnStr);
2434                 } catch (e) {
2435                         return false; // not a function
2436                 }
2437         };
2438
2439         var tryFunctionObject = function tryFunctionToStr(value) {
2440                 try {
2441                         if (isES6ClassFn(value)) { return false; }
2442                         fnToStr.call(value);
2443                         return true;
2444                 } catch (e) {
2445                         return false;
2446                 }
2447         };
2448         var toStr$4 = Object.prototype.toString;
2449         var fnClass = '[object Function]';
2450         var genClass = '[object GeneratorFunction]';
2451         var hasToStringTag = typeof Symbol === 'function' && typeof Symbol.toStringTag === 'symbol';
2452
2453         var isCallable = reflectApply
2454                 ? function isCallable(value) {
2455                         if (!value) { return false; }
2456                         if (typeof value !== 'function' && typeof value !== 'object') { return false; }
2457                         if (typeof value === 'function' && !value.prototype) { return true; }
2458                         try {
2459                                 reflectApply(value, null, badArrayLike);
2460                         } catch (e) {
2461                                 if (e !== isCallableMarker) { return false; }
2462                         }
2463                         return !isES6ClassFn(value);
2464                 }
2465                 : function isCallable(value) {
2466                         if (!value) { return false; }
2467                         if (typeof value !== 'function' && typeof value !== 'object') { return false; }
2468                         if (typeof value === 'function' && !value.prototype) { return true; }
2469                         if (hasToStringTag) { return tryFunctionObject(value); }
2470                         if (isES6ClassFn(value)) { return false; }
2471                         var strClass = toStr$4.call(value);
2472                         return strClass === fnClass || strClass === genClass;
2473                 };
2474
2475         var getDay = Date.prototype.getDay;
2476         var tryDateObject = function tryDateGetDayCall(value) {
2477                 try {
2478                         getDay.call(value);
2479                         return true;
2480                 } catch (e) {
2481                         return false;
2482                 }
2483         };
2484
2485         var toStr$5 = Object.prototype.toString;
2486         var dateClass = '[object Date]';
2487         var hasToStringTag$1 = typeof Symbol === 'function' && typeof Symbol.toStringTag === 'symbol';
2488
2489         var isDateObject = function isDateObject(value) {
2490                 if (typeof value !== 'object' || value === null) {
2491                         return false;
2492                 }
2493                 return hasToStringTag$1 ? tryDateObject(value) : toStr$5.call(value) === dateClass;
2494         };
2495
2496         var isSymbol$2 = createCommonjsModule(function (module) {
2497
2498         var toStr = Object.prototype.toString;
2499         var hasSymbols = hasSymbols$1();
2500
2501         if (hasSymbols) {
2502                 var symToStr = Symbol.prototype.toString;
2503                 var symStringRegex = /^Symbol\(.*\)$/;
2504                 var isSymbolObject = function isRealSymbolObject(value) {
2505                         if (typeof value.valueOf() !== 'symbol') {
2506                                 return false;
2507                         }
2508                         return symStringRegex.test(symToStr.call(value));
2509                 };
2510
2511                 module.exports = function isSymbol(value) {
2512                         if (typeof value === 'symbol') {
2513                                 return true;
2514                         }
2515                         if (toStr.call(value) !== '[object Symbol]') {
2516                                 return false;
2517                         }
2518                         try {
2519                                 return isSymbolObject(value);
2520                         } catch (e) {
2521                                 return false;
2522                         }
2523                 };
2524         } else {
2525
2526                 module.exports = function isSymbol(value) {
2527                         // this environment does not support Symbols.
2528                         return false ;
2529                 };
2530         }
2531         });
2532
2533         var hasSymbols$3 = typeof Symbol === 'function' && typeof Symbol.iterator === 'symbol';
2534
2535
2536
2537
2538
2539
2540         var ordinaryToPrimitive = function OrdinaryToPrimitive(O, hint) {
2541                 if (typeof O === 'undefined' || O === null) {
2542                         throw new TypeError('Cannot call method on ' + O);
2543                 }
2544                 if (typeof hint !== 'string' || (hint !== 'number' && hint !== 'string')) {
2545                         throw new TypeError('hint must be "string" or "number"');
2546                 }
2547                 var methodNames = hint === 'string' ? ['toString', 'valueOf'] : ['valueOf', 'toString'];
2548                 var method, result, i;
2549                 for (i = 0; i < methodNames.length; ++i) {
2550                         method = O[methodNames[i]];
2551                         if (isCallable(method)) {
2552                                 result = method.call(O);
2553                                 if (isPrimitive$1(result)) {
2554                                         return result;
2555                                 }
2556                         }
2557                 }
2558                 throw new TypeError('No default value');
2559         };
2560
2561         var GetMethod = function GetMethod(O, P) {
2562                 var func = O[P];
2563                 if (func !== null && typeof func !== 'undefined') {
2564                         if (!isCallable(func)) {
2565                                 throw new TypeError(func + ' returned for property ' + P + ' of object ' + O + ' is not a function');
2566                         }
2567                         return func;
2568                 }
2569                 return void 0;
2570         };
2571
2572         // http://www.ecma-international.org/ecma-262/6.0/#sec-toprimitive
2573         var es2015 = function ToPrimitive(input) {
2574                 if (isPrimitive$1(input)) {
2575                         return input;
2576                 }
2577                 var hint = 'default';
2578                 if (arguments.length > 1) {
2579                         if (arguments[1] === String) {
2580                                 hint = 'string';
2581                         } else if (arguments[1] === Number) {
2582                                 hint = 'number';
2583                         }
2584                 }
2585
2586                 var exoticToPrim;
2587                 if (hasSymbols$3) {
2588                         if (Symbol.toPrimitive) {
2589                                 exoticToPrim = GetMethod(input, Symbol.toPrimitive);
2590                         } else if (isSymbol$2(input)) {
2591                                 exoticToPrim = Symbol.prototype.valueOf;
2592                         }
2593                 }
2594                 if (typeof exoticToPrim !== 'undefined') {
2595                         var result = exoticToPrim.call(input, hint);
2596                         if (isPrimitive$1(result)) {
2597                                 return result;
2598                         }
2599                         throw new TypeError('unable to convert exotic object to primitive');
2600                 }
2601                 if (hint === 'default' && (isDateObject(input) || isSymbol$2(input))) {
2602                         hint = 'string';
2603                 }
2604                 return ordinaryToPrimitive(input, hint === 'default' ? 'number' : hint);
2605         };
2606
2607         // https://www.ecma-international.org/ecma-262/6.0/#sec-toprimitive
2608
2609         var ToPrimitive = function ToPrimitive(input) {
2610                 if (arguments.length > 1) {
2611                         return es2015(input, arguments[1]);
2612                 }
2613                 return es2015(input);
2614         };
2615
2616         var $TypeError$2 = GetIntrinsic('%TypeError%');
2617         var $Number$1 = GetIntrinsic('%Number%');
2618         var $RegExp = GetIntrinsic('%RegExp%');
2619         var $parseInteger = GetIntrinsic('%parseInt%');
2620
2621
2622
2623
2624
2625         var $strSlice = callBound('String.prototype.slice');
2626         var isBinary = regexTester(/^0b[01]+$/i);
2627         var isOctal = regexTester(/^0o[0-7]+$/i);
2628         var isInvalidHexLiteral = regexTester(/^[-+]0x[0-9a-f]+$/i);
2629         var nonWS = ['\u0085', '\u200b', '\ufffe'].join('');
2630         var nonWSregex = new $RegExp('[' + nonWS + ']', 'g');
2631         var hasNonWS = regexTester(nonWSregex);
2632
2633         // whitespace from: https://es5.github.io/#x15.5.4.20
2634         // implementation from https://github.com/es-shims/es5-shim/blob/v3.4.0/es5-shim.js#L1304-L1324
2635         var ws = [
2636                 '\x09\x0A\x0B\x0C\x0D\x20\xA0\u1680\u180E\u2000\u2001\u2002\u2003',
2637                 '\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000\u2028',
2638                 '\u2029\uFEFF'
2639         ].join('');
2640         var trimRegex = new RegExp('(^[' + ws + ']+)|([' + ws + ']+$)', 'g');
2641         var $replace$1 = callBound('String.prototype.replace');
2642         var $trim = function (value) {
2643                 return $replace$1(value, trimRegex, '');
2644         };
2645
2646
2647
2648         // https://www.ecma-international.org/ecma-262/6.0/#sec-tonumber
2649
2650         var ToNumber$1 = function ToNumber(argument) {
2651                 var value = isPrimitive(argument) ? argument : ToPrimitive(argument, $Number$1);
2652                 if (typeof value === 'symbol') {
2653                         throw new $TypeError$2('Cannot convert a Symbol value to a number');
2654                 }
2655                 if (typeof value === 'string') {
2656                         if (isBinary(value)) {
2657                                 return ToNumber($parseInteger($strSlice(value, 2), 2));
2658                         } else if (isOctal(value)) {
2659                                 return ToNumber($parseInteger($strSlice(value, 2), 8));
2660                         } else if (hasNonWS(value) || isInvalidHexLiteral(value)) {
2661                                 return NaN;
2662                         } else {
2663                                 var trimmed = $trim(value);
2664                                 if (trimmed !== value) {
2665                                         return ToNumber(trimmed);
2666                                 }
2667                         }
2668                 }
2669                 return $Number$1(value);
2670         };
2671
2672         // https://www.ecma-international.org/ecma-262/6.0/#sec-tointeger
2673
2674         var ToInteger$1 = function ToInteger$1(value) {
2675                 var number = ToNumber$1(value);
2676                 return ToInteger(number);
2677         };
2678
2679         var ToLength = function ToLength(argument) {
2680                 var len = ToInteger$1(argument);
2681                 if (len <= 0) { return 0; } // includes converting -0 to +0
2682                 if (len > maxSafeInteger) { return maxSafeInteger; }
2683                 return len;
2684         };
2685
2686         // http://www.ecma-international.org/ecma-262/5.1/#sec-9.11
2687
2688         var IsCallable = isCallable;
2689
2690         var implementation$3 = function find(predicate) {
2691                 var list = ToObject(this);
2692                 var length = ToLength(list.length);
2693                 if (!IsCallable(predicate)) {
2694                         throw new TypeError('Array#find: predicate must be a function');
2695                 }
2696                 if (length === 0) {
2697                         return void 0;
2698                 }
2699                 var thisArg;
2700                 if (arguments.length > 0) {
2701                         thisArg = arguments[1];
2702                 }
2703
2704                 for (var i = 0, value; i < length; i++) {
2705                         value = list[i];
2706                         // inlined for performance: if (Call(predicate, thisArg, [value, i, list])) {
2707                         if (predicate.apply(thisArg, [value, i, list])) {
2708                                 return value;
2709                         }
2710                 }
2711                 return void 0;
2712         };
2713
2714         var polyfill$4 = function getPolyfill() {
2715                 // Detect if an implementation exists
2716                 // Detect early implementations which skipped holes in sparse arrays
2717                 // eslint-disable-next-line no-sparse-arrays
2718                 var implemented = Array.prototype.find && [, 1].find(function () {
2719                         return true;
2720                 }) !== 1;
2721
2722                 // eslint-disable-next-line global-require
2723                 return implemented ? Array.prototype.find : implementation$3;
2724         };
2725
2726         var shim$8 = function shimArrayPrototypeFind() {
2727                 var polyfill = polyfill$4();
2728
2729                 defineProperties_1(Array.prototype, { find: polyfill }, {
2730                         find: function () {
2731                                 return Array.prototype.find !== polyfill;
2732                         }
2733                 });
2734
2735                 return polyfill;
2736         };
2737
2738         var slice$2 = Array.prototype.slice;
2739
2740         var polyfill$5 = polyfill$4();
2741
2742         var boundFindShim = function find(array, predicate) { // eslint-disable-line no-unused-vars
2743                 RequireObjectCoercible(array);
2744                 var args = slice$2.call(arguments, 1);
2745                 return polyfill$5.apply(array, args);
2746         };
2747
2748         defineProperties_1(boundFindShim, {
2749                 getPolyfill: polyfill$4,
2750                 implementation: implementation$3,
2751                 shim: shim$8
2752         });
2753
2754         var array_prototype_find = boundFindShim;
2755
2756         var implementation$4 = function findIndex(predicate) {
2757                 var list = ToObject(this);
2758                 var length = ToLength(list.length);
2759                 if (!IsCallable(predicate)) {
2760                         throw new TypeError('Array#findIndex: predicate must be a function');
2761                 }
2762
2763                 if (length === 0) {
2764                         return -1;
2765                 }
2766
2767                 var thisArg;
2768                 if (arguments.length > 0) {
2769                         thisArg = arguments[1];
2770                 }
2771
2772                 for (var i = 0, value; i < length; i++) {
2773                         value = list[i];
2774                         // inlined for performance: if (Call(predicate, thisArg, [value, i, list])) return i;
2775                         if (predicate.apply(thisArg, [value, i, list])) {
2776                                 return i;
2777                         }
2778                 }
2779
2780                 return -1;
2781         };
2782
2783         var polyfill$6 = function getPolyfill() {
2784                 // Detect if an implementation exists
2785                 // Detect early implementations which skipped holes in sparse arrays
2786                 // eslint-disable-next-line no-sparse-arrays
2787                 var implemented = Array.prototype.findIndex && ([, 1].findIndex(function (item, idx) {
2788                         return idx === 0;
2789                 }) === 0);
2790
2791                 return implemented ? Array.prototype.findIndex : implementation$4;
2792         };
2793
2794         var shim$9 = function shimFindIndex() {
2795                 var polyfill = polyfill$6();
2796
2797                 defineProperties_1(Array.prototype, { findIndex: polyfill }, {
2798                         findIndex: function () {
2799                                 return Array.prototype.findIndex !== polyfill;
2800                         }
2801                 });
2802
2803                 return polyfill;
2804         };
2805
2806         var slice$3 = Array.prototype.slice;
2807
2808         var polyfill$7 = polyfill$6();
2809
2810         var boundShim = function findIndex(array, predicate) { // eslint-disable-line no-unused-vars
2811                 RequireObjectCoercible(array);
2812                 var args = slice$3.call(arguments, 1);
2813                 return polyfill$7.apply(array, args);
2814         };
2815
2816         defineProperties_1(boundShim, {
2817                 getPolyfill: polyfill$6,
2818                 implementation: implementation$4,
2819                 shim: shim$9
2820         });
2821
2822         var array_prototype_findindex = boundShim;
2823
2824         var $apply$1 = GetIntrinsic('%Reflect.apply%', true) || callBound('%Function.prototype.apply%');
2825
2826         // https://www.ecma-international.org/ecma-262/6.0/#sec-call
2827
2828         var Call = function Call(F, V) {
2829                 var args = arguments.length > 2 ? arguments[2] : [];
2830                 return $apply$1(F, V, args);
2831         };
2832
2833         var $defineProperty = GetIntrinsic('%Object.defineProperty%', true);
2834
2835         if ($defineProperty) {
2836                 try {
2837                         $defineProperty({}, 'a', { value: 1 });
2838                 } catch (e) {
2839                         // IE 8 has a broken defineProperty
2840                         $defineProperty = null;
2841                 }
2842         }
2843
2844
2845
2846         var $isEnumerable = callBound('Object.prototype.propertyIsEnumerable');
2847
2848         // eslint-disable-next-line max-params
2849         var DefineOwnProperty = function DefineOwnProperty(IsDataDescriptor, SameValue, FromPropertyDescriptor, O, P, desc) {
2850                 if (!$defineProperty) {
2851                         if (!IsDataDescriptor(desc)) {
2852                                 // ES3 does not support getters/setters
2853                                 return false;
2854                         }
2855                         if (!desc['[[Configurable]]'] || !desc['[[Writable]]']) {
2856                                 return false;
2857                         }
2858
2859                         // fallback for ES3
2860                         if (P in O && $isEnumerable(O, P) !== !!desc['[[Enumerable]]']) {
2861                                 // a non-enumerable existing property
2862                                 return false;
2863                         }
2864
2865                         // property does not exist at all, or exists but is enumerable
2866                         var V = desc['[[Value]]'];
2867                         // eslint-disable-next-line no-param-reassign
2868                         O[P] = V; // will use [[Define]]
2869                         return SameValue(O[P], V);
2870                 }
2871                 $defineProperty(O, P, FromPropertyDescriptor(desc));
2872                 return true;
2873         };
2874
2875         var src = functionBind.call(Function.call, Object.prototype.hasOwnProperty);
2876
2877         var $TypeError$3 = GetIntrinsic('%TypeError%');
2878         var $SyntaxError = GetIntrinsic('%SyntaxError%');
2879
2880
2881
2882         var predicates = {
2883                 // https://ecma-international.org/ecma-262/6.0/#sec-property-descriptor-specification-type
2884                 'Property Descriptor': function isPropertyDescriptor(Type, Desc) {
2885                         if (Type(Desc) !== 'Object') {
2886                                 return false;
2887                         }
2888                         var allowed = {
2889                                 '[[Configurable]]': true,
2890                                 '[[Enumerable]]': true,
2891                                 '[[Get]]': true,
2892                                 '[[Set]]': true,
2893                                 '[[Value]]': true,
2894                                 '[[Writable]]': true
2895                         };
2896
2897                         for (var key in Desc) { // eslint-disable-line
2898                                 if (src(Desc, key) && !allowed[key]) {
2899                                         return false;
2900                                 }
2901                         }
2902
2903                         var isData = src(Desc, '[[Value]]');
2904                         var IsAccessor = src(Desc, '[[Get]]') || src(Desc, '[[Set]]');
2905                         if (isData && IsAccessor) {
2906                                 throw new $TypeError$3('Property Descriptors may not be both accessor and data descriptors');
2907                         }
2908                         return true;
2909                 }
2910         };
2911
2912         var assertRecord = function assertRecord(Type, recordType, argumentName, value) {
2913                 var predicate = predicates[recordType];
2914                 if (typeof predicate !== 'function') {
2915                         throw new $SyntaxError('unknown record type: ' + recordType);
2916                 }
2917                 if (!predicate(Type, value)) {
2918                         throw new $TypeError$3(argumentName + ' must be a ' + recordType);
2919                 }
2920         };
2921
2922         // https://www.ecma-international.org/ecma-262/5.1/#sec-8
2923
2924         var Type = function Type(x) {
2925                 if (x === null) {
2926                         return 'Null';
2927                 }
2928                 if (typeof x === 'undefined') {
2929                         return 'Undefined';
2930                 }
2931                 if (typeof x === 'function' || typeof x === 'object') {
2932                         return 'Object';
2933                 }
2934                 if (typeof x === 'number') {
2935                         return 'Number';
2936                 }
2937                 if (typeof x === 'boolean') {
2938                         return 'Boolean';
2939                 }
2940                 if (typeof x === 'string') {
2941                         return 'String';
2942                 }
2943         };
2944
2945         // https://ecma-international.org/ecma-262/6.0/#sec-ecmascript-data-types-and-values
2946
2947         var Type$1 = function Type$1(x) {
2948                 if (typeof x === 'symbol') {
2949                         return 'Symbol';
2950                 }
2951                 return Type(x);
2952         };
2953
2954         // https://www.ecma-international.org/ecma-262/6.0/#sec-frompropertydescriptor
2955
2956         var FromPropertyDescriptor = function FromPropertyDescriptor(Desc) {
2957                 if (typeof Desc === 'undefined') {
2958                         return Desc;
2959                 }
2960
2961                 assertRecord(Type$1, 'Property Descriptor', 'Desc', Desc);
2962
2963                 var obj = {};
2964                 if ('[[Value]]' in Desc) {
2965                         obj.value = Desc['[[Value]]'];
2966                 }
2967                 if ('[[Writable]]' in Desc) {
2968                         obj.writable = Desc['[[Writable]]'];
2969                 }
2970                 if ('[[Get]]' in Desc) {
2971                         obj.get = Desc['[[Get]]'];
2972                 }
2973                 if ('[[Set]]' in Desc) {
2974                         obj.set = Desc['[[Set]]'];
2975                 }
2976                 if ('[[Enumerable]]' in Desc) {
2977                         obj.enumerable = Desc['[[Enumerable]]'];
2978                 }
2979                 if ('[[Configurable]]' in Desc) {
2980                         obj.configurable = Desc['[[Configurable]]'];
2981                 }
2982                 return obj;
2983         };
2984
2985         var $gOPD$1 = GetIntrinsic('%Object.getOwnPropertyDescriptor%');
2986         if ($gOPD$1) {
2987                 try {
2988                         $gOPD$1([], 'length');
2989                 } catch (e) {
2990                         // IE 8 has a broken gOPD
2991                         $gOPD$1 = null;
2992                 }
2993         }
2994
2995         var getOwnPropertyDescriptor = $gOPD$1;
2996
2997         var $Array = GetIntrinsic('%Array%');
2998
2999         // eslint-disable-next-line global-require
3000         var toStr$6 = !$Array.isArray && callBound('Object.prototype.toString');
3001
3002         // https://www.ecma-international.org/ecma-262/6.0/#sec-isarray
3003
3004         var IsArray = $Array.isArray || function IsArray(argument) {
3005                 return toStr$6(argument) === '[object Array]';
3006         };
3007
3008         // https://www.ecma-international.org/ecma-262/6.0/#sec-ispropertykey
3009
3010         var IsPropertyKey = function IsPropertyKey(argument) {
3011                 return typeof argument === 'string' || typeof argument === 'symbol';
3012         };
3013
3014         var hasSymbols$4 = hasSymbols$1();
3015         var hasToStringTag$2 = hasSymbols$4 && typeof Symbol.toStringTag === 'symbol';
3016         var regexExec;
3017         var isRegexMarker;
3018         var badStringifier;
3019
3020         if (hasToStringTag$2) {
3021                 regexExec = Function.call.bind(RegExp.prototype.exec);
3022                 isRegexMarker = {};
3023
3024                 var throwRegexMarker = function () {
3025                         throw isRegexMarker;
3026                 };
3027                 badStringifier = {
3028                         toString: throwRegexMarker,
3029                         valueOf: throwRegexMarker
3030                 };
3031
3032                 if (typeof Symbol.toPrimitive === 'symbol') {
3033                         badStringifier[Symbol.toPrimitive] = throwRegexMarker;
3034                 }
3035         }
3036
3037         var toStr$7 = Object.prototype.toString;
3038         var regexClass = '[object RegExp]';
3039
3040         var isRegex = hasToStringTag$2
3041                 // eslint-disable-next-line consistent-return
3042                 ? function isRegex(value) {
3043                         if (!value || typeof value !== 'object') {
3044                                 return false;
3045                         }
3046
3047                         try {
3048                                 regexExec(value, badStringifier);
3049                         } catch (e) {
3050                                 return e === isRegexMarker;
3051                         }
3052                 }
3053                 : function isRegex(value) {
3054                         // In older browsers, typeof regex incorrectly returns 'function'
3055                         if (!value || (typeof value !== 'object' && typeof value !== 'function')) {
3056                                 return false;
3057                         }
3058
3059                         return toStr$7.call(value) === regexClass;
3060                 };
3061
3062         // http://www.ecma-international.org/ecma-262/5.1/#sec-9.2
3063
3064         var ToBoolean = function ToBoolean(value) { return !!value; };
3065
3066         var $match = GetIntrinsic('%Symbol.match%', true);
3067
3068
3069
3070
3071
3072         // https://ecma-international.org/ecma-262/6.0/#sec-isregexp
3073
3074         var IsRegExp = function IsRegExp(argument) {
3075                 if (!argument || typeof argument !== 'object') {
3076                         return false;
3077                 }
3078                 if ($match) {
3079                         var isRegExp = argument[$match];
3080                         if (typeof isRegExp !== 'undefined') {
3081                                 return ToBoolean(isRegExp);
3082                         }
3083                 }
3084                 return isRegex(argument);
3085         };
3086
3087         var $TypeError$4 = GetIntrinsic('%TypeError%');
3088
3089
3090
3091
3092
3093         // https://ecma-international.org/ecma-262/5.1/#sec-8.10.5
3094
3095         var ToPropertyDescriptor = function ToPropertyDescriptor(Obj) {
3096                 if (Type$1(Obj) !== 'Object') {
3097                         throw new $TypeError$4('ToPropertyDescriptor requires an object');
3098                 }
3099
3100                 var desc = {};
3101                 if (src(Obj, 'enumerable')) {
3102                         desc['[[Enumerable]]'] = ToBoolean(Obj.enumerable);
3103                 }
3104                 if (src(Obj, 'configurable')) {
3105                         desc['[[Configurable]]'] = ToBoolean(Obj.configurable);
3106                 }
3107                 if (src(Obj, 'value')) {
3108                         desc['[[Value]]'] = Obj.value;
3109                 }
3110                 if (src(Obj, 'writable')) {
3111                         desc['[[Writable]]'] = ToBoolean(Obj.writable);
3112                 }
3113                 if (src(Obj, 'get')) {
3114                         var getter = Obj.get;
3115                         if (typeof getter !== 'undefined' && !IsCallable(getter)) {
3116                                 throw new TypeError('getter must be a function');
3117                         }
3118                         desc['[[Get]]'] = getter;
3119                 }
3120                 if (src(Obj, 'set')) {
3121                         var setter = Obj.set;
3122                         if (typeof setter !== 'undefined' && !IsCallable(setter)) {
3123                                 throw new $TypeError$4('setter must be a function');
3124                         }
3125                         desc['[[Set]]'] = setter;
3126                 }
3127
3128                 if ((src(desc, '[[Get]]') || src(desc, '[[Set]]')) && (src(desc, '[[Value]]') || src(desc, '[[Writable]]'))) {
3129                         throw new $TypeError$4('Invalid property descriptor. Cannot both specify accessors and a value or writable attribute');
3130                 }
3131                 return desc;
3132         };
3133
3134         var $TypeError$5 = GetIntrinsic('%TypeError%');
3135
3136
3137
3138         var $isEnumerable$1 = callBound('Object.prototype.propertyIsEnumerable');
3139
3140
3141
3142
3143
3144
3145
3146
3147
3148         // https://www.ecma-international.org/ecma-262/6.0/#sec-ordinarygetownproperty
3149
3150         var OrdinaryGetOwnProperty = function OrdinaryGetOwnProperty(O, P) {
3151                 if (Type$1(O) !== 'Object') {
3152                         throw new $TypeError$5('Assertion failed: O must be an Object');
3153                 }
3154                 if (!IsPropertyKey(P)) {
3155                         throw new $TypeError$5('Assertion failed: P must be a Property Key');
3156                 }
3157                 if (!src(O, P)) {
3158                         return void 0;
3159                 }
3160                 if (!getOwnPropertyDescriptor) {
3161                         // ES3 / IE 8 fallback
3162                         var arrayLength = IsArray(O) && P === 'length';
3163                         var regexLastIndex = IsRegExp(O) && P === 'lastIndex';
3164                         return {
3165                                 '[[Configurable]]': !(arrayLength || regexLastIndex),
3166                                 '[[Enumerable]]': $isEnumerable$1(O, P),
3167                                 '[[Value]]': O[P],
3168                                 '[[Writable]]': true
3169                         };
3170                 }
3171                 return ToPropertyDescriptor(getOwnPropertyDescriptor(O, P));
3172         };
3173
3174         // https://www.ecma-international.org/ecma-262/6.0/#sec-isdatadescriptor
3175
3176         var IsDataDescriptor = function IsDataDescriptor(Desc) {
3177                 if (typeof Desc === 'undefined') {
3178                         return false;
3179                 }
3180
3181                 assertRecord(Type$1, 'Property Descriptor', 'Desc', Desc);
3182
3183                 if (!src(Desc, '[[Value]]') && !src(Desc, '[[Writable]]')) {
3184                         return false;
3185                 }
3186
3187                 return true;
3188         };
3189
3190         var $Object$1 = GetIntrinsic('%Object%');
3191
3192
3193
3194         var $preventExtensions = $Object$1.preventExtensions;
3195         var $isExtensible = $Object$1.isExtensible;
3196
3197         // https://www.ecma-international.org/ecma-262/6.0/#sec-isextensible-o
3198
3199         var IsExtensible = $preventExtensions
3200                 ? function IsExtensible(obj) {
3201                         return !isPrimitive(obj) && $isExtensible(obj);
3202                 }
3203                 : function IsExtensible(obj) {
3204                         return !isPrimitive(obj);
3205                 };
3206
3207         // http://www.ecma-international.org/ecma-262/5.1/#sec-9.12
3208
3209         var SameValue = function SameValue(x, y) {
3210                 if (x === y) { // 0 === -0, but they are not identical.
3211                         if (x === 0) { return 1 / x === 1 / y; }
3212                         return true;
3213                 }
3214                 return _isNaN(x) && _isNaN(y);
3215         };
3216
3217         var $TypeError$6 = GetIntrinsic('%TypeError%');
3218
3219
3220
3221
3222
3223
3224
3225
3226
3227
3228
3229         // https://www.ecma-international.org/ecma-262/6.0/#sec-createdataproperty
3230
3231         var CreateDataProperty = function CreateDataProperty(O, P, V) {
3232                 if (Type$1(O) !== 'Object') {
3233                         throw new $TypeError$6('Assertion failed: Type(O) is not Object');
3234                 }
3235                 if (!IsPropertyKey(P)) {
3236                         throw new $TypeError$6('Assertion failed: IsPropertyKey(P) is not true');
3237                 }
3238                 var oldDesc = OrdinaryGetOwnProperty(O, P);
3239                 var extensible = !oldDesc || IsExtensible(O);
3240                 var immutable = oldDesc && (!oldDesc['[[Writable]]'] || !oldDesc['[[Configurable]]']);
3241                 if (immutable || !extensible) {
3242                         return false;
3243                 }
3244                 return DefineOwnProperty(
3245                         IsDataDescriptor,
3246                         SameValue,
3247                         FromPropertyDescriptor,
3248                         O,
3249                         P,
3250                         {
3251                                 '[[Configurable]]': true,
3252                                 '[[Enumerable]]': true,
3253                                 '[[Value]]': V,
3254                                 '[[Writable]]': true
3255                         }
3256                 );
3257         };
3258
3259         var $TypeError$7 = GetIntrinsic('%TypeError%');
3260
3261
3262
3263
3264
3265         // // https://ecma-international.org/ecma-262/6.0/#sec-createdatapropertyorthrow
3266
3267         var CreateDataPropertyOrThrow = function CreateDataPropertyOrThrow(O, P, V) {
3268                 if (Type$1(O) !== 'Object') {
3269                         throw new $TypeError$7('Assertion failed: Type(O) is not Object');
3270                 }
3271                 if (!IsPropertyKey(P)) {
3272                         throw new $TypeError$7('Assertion failed: IsPropertyKey(P) is not true');
3273                 }
3274                 var success = CreateDataProperty(O, P, V);
3275                 if (!success) {
3276                         throw new $TypeError$7('unable to create data property');
3277                 }
3278                 return success;
3279         };
3280
3281         var hasMap = typeof Map === 'function' && Map.prototype;
3282         var mapSizeDescriptor = Object.getOwnPropertyDescriptor && hasMap ? Object.getOwnPropertyDescriptor(Map.prototype, 'size') : null;
3283         var mapSize = hasMap && mapSizeDescriptor && typeof mapSizeDescriptor.get === 'function' ? mapSizeDescriptor.get : null;
3284         var mapForEach = hasMap && Map.prototype.forEach;
3285         var hasSet = typeof Set === 'function' && Set.prototype;
3286         var setSizeDescriptor = Object.getOwnPropertyDescriptor && hasSet ? Object.getOwnPropertyDescriptor(Set.prototype, 'size') : null;
3287         var setSize = hasSet && setSizeDescriptor && typeof setSizeDescriptor.get === 'function' ? setSizeDescriptor.get : null;
3288         var setForEach = hasSet && Set.prototype.forEach;
3289         var booleanValueOf = Boolean.prototype.valueOf;
3290         var objectToString$1 = Object.prototype.toString;
3291
3292         var objectInspect = function inspect_ (obj, opts, depth, seen) {
3293             if (typeof obj === 'undefined') {
3294                 return 'undefined';
3295             }
3296             if (obj === null) {
3297                 return 'null';
3298             }
3299             if (typeof obj === 'boolean') {
3300                 return obj ? 'true' : 'false';
3301             }
3302             if (typeof obj === 'string') {
3303                 return inspectString(obj);
3304             }
3305             if (typeof obj === 'number') {
3306               if (obj === 0) {
3307                 return Infinity / obj > 0 ? '0' : '-0';
3308               }
3309               return String(obj);
3310             }
3311
3312             if (!opts) { opts = {}; }
3313
3314             var maxDepth = typeof opts.depth === 'undefined' ? 5 : opts.depth;
3315             if (typeof depth === 'undefined') { depth = 0; }
3316             if (depth >= maxDepth && maxDepth > 0 && typeof obj === 'object') {
3317                 return '[Object]';
3318             }
3319
3320             if (typeof seen === 'undefined') { seen = []; }
3321             else if (indexOf$2(seen, obj) >= 0) {
3322                 return '[Circular]';
3323             }
3324
3325             function inspect (value, from) {
3326                 if (from) {
3327                     seen = seen.slice();
3328                     seen.push(from);
3329                 }
3330                 return inspect_(value, opts, depth + 1, seen);
3331             }
3332
3333             if (typeof obj === 'function') {
3334                 var name = nameOf(obj);
3335                 return '[Function' + (name ? ': ' + name : '') + ']';
3336             }
3337             if (isSymbol$3(obj)) {
3338                 var symString = Symbol.prototype.toString.call(obj);
3339                 return typeof obj === 'object' ? markBoxed(symString) : symString;
3340             }
3341             if (isElement(obj)) {
3342                 var s = '<' + String(obj.nodeName).toLowerCase();
3343                 var attrs = obj.attributes || [];
3344                 for (var i = 0; i < attrs.length; i++) {
3345                     s += ' ' + attrs[i].name + '="' + quote(attrs[i].value) + '"';
3346                 }
3347                 s += '>';
3348                 if (obj.childNodes && obj.childNodes.length) { s += '...'; }
3349                 s += '</' + String(obj.nodeName).toLowerCase() + '>';
3350                 return s;
3351             }
3352             if (isArray$3(obj)) {
3353                 if (obj.length === 0) { return '[]'; }
3354                 return '[ ' + arrObjKeys(obj, inspect).join(', ') + ' ]';
3355             }
3356             if (isError(obj)) {
3357                 var parts = arrObjKeys(obj, inspect);
3358                 if (parts.length === 0) { return '[' + String(obj) + ']'; }
3359                 return '{ [' + String(obj) + '] ' + parts.join(', ') + ' }';
3360             }
3361             if (typeof obj === 'object' && typeof obj.inspect === 'function') {
3362                 return obj.inspect();
3363             }
3364             if (isMap(obj)) {
3365                 var parts = [];
3366                 mapForEach.call(obj, function (value, key) {
3367                     parts.push(inspect(key, obj) + ' => ' + inspect(value, obj));
3368                 });
3369                 return collectionOf('Map', mapSize.call(obj), parts);
3370             }
3371             if (isSet(obj)) {
3372                 var parts = [];
3373                 setForEach.call(obj, function (value ) {
3374                     parts.push(inspect(value, obj));
3375                 });
3376                 return collectionOf('Set', setSize.call(obj), parts);
3377             }
3378             if (isNumber(obj)) {
3379                 return markBoxed(Number(obj));
3380             }
3381             if (isBoolean(obj)) {
3382                 return markBoxed(booleanValueOf.call(obj));
3383             }
3384             if (isString$1(obj)) {
3385                 return markBoxed(inspect(String(obj)));
3386             }
3387             if (!isDate(obj) && !isRegExp(obj)) {
3388                 var xs = arrObjKeys(obj, inspect);
3389                 if (xs.length === 0) { return '{}'; }
3390                 return '{ ' + xs.join(', ') + ' }';
3391             }
3392             return String(obj);
3393         };
3394
3395         function quote (s) {
3396             return String(s).replace(/"/g, '&quot;');
3397         }
3398
3399         function isArray$3 (obj) { return toStr$8(obj) === '[object Array]' }
3400         function isDate (obj) { return toStr$8(obj) === '[object Date]' }
3401         function isRegExp (obj) { return toStr$8(obj) === '[object RegExp]' }
3402         function isError (obj) { return toStr$8(obj) === '[object Error]' }
3403         function isSymbol$3 (obj) { return toStr$8(obj) === '[object Symbol]' }
3404         function isString$1 (obj) { return toStr$8(obj) === '[object String]' }
3405         function isNumber (obj) { return toStr$8(obj) === '[object Number]' }
3406         function isBoolean (obj) { return toStr$8(obj) === '[object Boolean]' }
3407
3408         var hasOwn = Object.prototype.hasOwnProperty || function (key) { return key in this; };
3409         function has$1 (obj, key) {
3410             return hasOwn.call(obj, key);
3411         }
3412
3413         function toStr$8 (obj) {
3414             return objectToString$1.call(obj);
3415         }
3416
3417         function nameOf (f) {
3418             if (f.name) { return f.name; }
3419             var m = String(f).match(/^function\s*([\w$]+)/);
3420             if (m) { return m[1]; }
3421         }
3422
3423         function indexOf$2 (xs, x) {
3424             if (xs.indexOf) { return xs.indexOf(x); }
3425             for (var i = 0, l = xs.length; i < l; i++) {
3426                 if (xs[i] === x) { return i; }
3427             }
3428             return -1;
3429         }
3430
3431         function isMap (x) {
3432             if (!mapSize) {
3433                 return false;
3434             }
3435             try {
3436                 mapSize.call(x);
3437                 try {
3438                     setSize.call(x);
3439                 } catch (s) {
3440                     return true;
3441                 }
3442                 return x instanceof Map; // core-js workaround, pre-v2.5.0
3443             } catch (e) {}
3444             return false;
3445         }
3446
3447         function isSet (x) {
3448             if (!setSize) {
3449                 return false;
3450             }
3451             try {
3452                 setSize.call(x);
3453                 try {
3454                     mapSize.call(x);
3455                 } catch (m) {
3456                     return true;
3457                 }
3458                 return x instanceof Set; // core-js workaround, pre-v2.5.0
3459             } catch (e) {}
3460             return false;
3461         }
3462
3463         function isElement (x) {
3464             if (!x || typeof x !== 'object') { return false; }
3465             if (typeof HTMLElement !== 'undefined' && x instanceof HTMLElement) {
3466                 return true;
3467             }
3468             return typeof x.nodeName === 'string'
3469                 && typeof x.getAttribute === 'function'
3470             ;
3471         }
3472
3473         function inspectString (str) {
3474             var s = str.replace(/(['\\])/g, '\\$1').replace(/[\x00-\x1f]/g, lowbyte);
3475             return "'" + s + "'";
3476         }
3477
3478         function lowbyte (c) {
3479             var n = c.charCodeAt(0);
3480             var x = { 8: 'b', 9: 't', 10: 'n', 12: 'f', 13: 'r' }[n];
3481             if (x) { return '\\' + x; }
3482             return '\\x' + (n < 0x10 ? '0' : '') + n.toString(16);
3483         }
3484
3485         function markBoxed (str) {
3486             return 'Object(' + str + ')';
3487         }
3488
3489         function collectionOf (type, size, entries) {
3490             return type + ' (' + size + ') {' + entries.join(', ') + '}';
3491         }
3492
3493         function arrObjKeys (obj, inspect) {
3494             var isArr = isArray$3(obj);
3495             var xs = [];
3496             if (isArr) {
3497                 xs.length = obj.length;
3498                 for (var i = 0; i < obj.length; i++) {
3499                     xs[i] = has$1(obj, i) ? inspect(obj[i], obj) : '';
3500                 }
3501             }
3502             for (var key in obj) {
3503                 if (!has$1(obj, key)) { continue; }
3504                 if (isArr && String(Number(key)) === key && key < obj.length) { continue; }
3505                 if (/[^\w$]/.test(key)) {
3506                     xs.push(inspect(key, obj) + ': ' + inspect(obj[key], obj));
3507                 } else {
3508                     xs.push(key + ': ' + inspect(obj[key], obj));
3509                 }
3510             }
3511             return xs;
3512         }
3513
3514         var $TypeError$8 = GetIntrinsic('%TypeError%');
3515
3516
3517
3518
3519
3520
3521         /**
3522          * 7.3.1 Get (O, P) - https://ecma-international.org/ecma-262/6.0/#sec-get-o-p
3523          * 1. Assert: Type(O) is Object.
3524          * 2. Assert: IsPropertyKey(P) is true.
3525          * 3. Return O.[[Get]](P, O).
3526          */
3527
3528         var Get = function Get(O, P) {
3529                 // 7.3.1.1
3530                 if (Type$1(O) !== 'Object') {
3531                         throw new $TypeError$8('Assertion failed: Type(O) is not Object');
3532                 }
3533                 // 7.3.1.2
3534                 if (!IsPropertyKey(P)) {
3535                         throw new $TypeError$8('Assertion failed: IsPropertyKey(P) is not true, got ' + objectInspect(P));
3536                 }
3537                 // 7.3.1.3
3538                 return O[P];
3539         };
3540
3541         var $TypeError$9 = GetIntrinsic('%TypeError%');
3542
3543         var isPropertyDescriptor = function IsPropertyDescriptor(ES, Desc) {
3544                 if (ES.Type(Desc) !== 'Object') {
3545                         return false;
3546                 }
3547                 var allowed = {
3548                         '[[Configurable]]': true,
3549                         '[[Enumerable]]': true,
3550                         '[[Get]]': true,
3551                         '[[Set]]': true,
3552                         '[[Value]]': true,
3553                         '[[Writable]]': true
3554                 };
3555
3556                 for (var key in Desc) { // eslint-disable-line no-restricted-syntax
3557                         if (src(Desc, key) && !allowed[key]) {
3558                                 return false;
3559                         }
3560                 }
3561
3562                 if (ES.IsDataDescriptor(Desc) && ES.IsAccessorDescriptor(Desc)) {
3563                         throw new $TypeError$9('Property Descriptors may not be both accessor and data descriptors');
3564                 }
3565                 return true;
3566         };
3567
3568         // https://www.ecma-international.org/ecma-262/6.0/#sec-isaccessordescriptor
3569
3570         var IsAccessorDescriptor = function IsAccessorDescriptor(Desc) {
3571                 if (typeof Desc === 'undefined') {
3572                         return false;
3573                 }
3574
3575                 assertRecord(Type$1, 'Property Descriptor', 'Desc', Desc);
3576
3577                 if (!src(Desc, '[[Get]]') && !src(Desc, '[[Set]]')) {
3578                         return false;
3579                 }
3580
3581                 return true;
3582         };
3583
3584         var $TypeError$a = GetIntrinsic('%TypeError%');
3585
3586
3587
3588
3589
3590
3591
3592
3593
3594
3595
3596
3597         // https://www.ecma-international.org/ecma-262/6.0/#sec-definepropertyorthrow
3598
3599         var DefinePropertyOrThrow = function DefinePropertyOrThrow(O, P, desc) {
3600                 if (Type$1(O) !== 'Object') {
3601                         throw new $TypeError$a('Assertion failed: Type(O) is not Object');
3602                 }
3603
3604                 if (!IsPropertyKey(P)) {
3605                         throw new $TypeError$a('Assertion failed: IsPropertyKey(P) is not true');
3606                 }
3607
3608                 var Desc = isPropertyDescriptor({
3609                         Type: Type$1,
3610                         IsDataDescriptor: IsDataDescriptor,
3611                         IsAccessorDescriptor: IsAccessorDescriptor
3612                 }, desc) ? desc : ToPropertyDescriptor(desc);
3613                 if (!isPropertyDescriptor({
3614                         Type: Type$1,
3615                         IsDataDescriptor: IsDataDescriptor,
3616                         IsAccessorDescriptor: IsAccessorDescriptor
3617                 }, Desc)) {
3618                         throw new $TypeError$a('Assertion failed: Desc is not a valid Property Descriptor');
3619                 }
3620
3621                 return DefineOwnProperty(
3622                         IsDataDescriptor,
3623                         SameValue,
3624                         FromPropertyDescriptor,
3625                         O,
3626                         P,
3627                         Desc
3628                 );
3629         };
3630
3631         var IsConstructor = createCommonjsModule(function (module) {
3632
3633
3634
3635         var $construct = GetIntrinsic('%Reflect.construct%', true);
3636
3637         var DefinePropertyOrThrow$1 = DefinePropertyOrThrow;
3638         try {
3639                 DefinePropertyOrThrow$1({}, '', { '[[Get]]': function () {} });
3640         } catch (e) {
3641                 // Accessor properties aren't supported
3642                 DefinePropertyOrThrow$1 = null;
3643         }
3644
3645         // https://www.ecma-international.org/ecma-262/6.0/#sec-isconstructor
3646
3647         if (DefinePropertyOrThrow$1 && $construct) {
3648                 var isConstructorMarker = {};
3649                 var badArrayLike = {};
3650                 DefinePropertyOrThrow$1(badArrayLike, 'length', {
3651                         '[[Get]]': function () {
3652                                 throw isConstructorMarker;
3653                         },
3654                         '[[Enumerable]]': true
3655                 });
3656
3657                 module.exports = function IsConstructor(argument) {
3658                         try {
3659                                 // `Reflect.construct` invokes `IsConstructor(target)` before `Get(args, 'length')`:
3660                                 $construct(argument, badArrayLike);
3661                         } catch (err) {
3662                                 return err === isConstructorMarker;
3663                         }
3664                 };
3665         } else {
3666                 module.exports = function IsConstructor(argument) {
3667                         // unfortunately there's no way to truly check this without try/catch `new argument` in old environments
3668                         return typeof argument === 'function' && !!argument.prototype;
3669                 };
3670         }
3671         });
3672
3673         var $String = GetIntrinsic('%String%');
3674         var $TypeError$b = GetIntrinsic('%TypeError%');
3675
3676         // https://www.ecma-international.org/ecma-262/6.0/#sec-tostring
3677
3678         var ToString = function ToString(argument) {
3679                 if (typeof argument === 'symbol') {
3680                         throw new $TypeError$b('Cannot convert a Symbol value to a string');
3681                 }
3682                 return $String(argument);
3683         };
3684
3685         var hasToStringTag$3 = typeof Symbol === 'function' && typeof Symbol.toStringTag === 'symbol';
3686         var toStr$9 = Object.prototype.toString;
3687
3688         var isStandardArguments = function isArguments(value) {
3689                 if (hasToStringTag$3 && value && typeof value === 'object' && Symbol.toStringTag in value) {
3690                         return false;
3691                 }
3692                 return toStr$9.call(value) === '[object Arguments]';
3693         };
3694
3695         var isLegacyArguments = function isArguments(value) {
3696                 if (isStandardArguments(value)) {
3697                         return true;
3698                 }
3699                 return value !== null &&
3700                         typeof value === 'object' &&
3701                         typeof value.length === 'number' &&
3702                         value.length >= 0 &&
3703                         toStr$9.call(value) !== '[object Array]' &&
3704                         toStr$9.call(value.callee) === '[object Function]';
3705         };
3706
3707         var supportsStandardArguments = (function () {
3708                 return isStandardArguments(arguments);
3709         }());
3710
3711         isStandardArguments.isLegacyArguments = isLegacyArguments; // for tests
3712
3713         var isArguments$2 = supportsStandardArguments ? isStandardArguments : isLegacyArguments;
3714
3715         var toString = {}.toString;
3716
3717         var isarray = Array.isArray || function (arr) {
3718           return toString.call(arr) == '[object Array]';
3719         };
3720
3721         var strValue = String.prototype.valueOf;
3722         var tryStringObject = function tryStringObject(value) {
3723                 try {
3724                         strValue.call(value);
3725                         return true;
3726                 } catch (e) {
3727                         return false;
3728                 }
3729         };
3730         var toStr$a = Object.prototype.toString;
3731         var strClass = '[object String]';
3732         var hasToStringTag$4 = typeof Symbol === 'function' && typeof Symbol.toStringTag === 'symbol';
3733
3734         var isString$2 = function isString(value) {
3735                 if (typeof value === 'string') {
3736                         return true;
3737                 }
3738                 if (typeof value !== 'object') {
3739                         return false;
3740                 }
3741                 return hasToStringTag$4 ? tryStringObject(value) : toStr$a.call(value) === strClass;
3742         };
3743
3744         var $Map = typeof Map === 'function' && Map.prototype ? Map : null;
3745         var $Set = typeof Set === 'function' && Set.prototype ? Set : null;
3746
3747         var exported;
3748
3749         if (!$Map) {
3750                 // eslint-disable-next-line no-unused-vars
3751                 exported = function isMap(x) {
3752                         // `Map` is not present in this environment.
3753                         return false;
3754                 };
3755         }
3756
3757         var $mapHas = $Map ? Map.prototype.has : null;
3758         var $setHas = $Set ? Set.prototype.has : null;
3759         if (!exported && !$mapHas) {
3760                 // eslint-disable-next-line no-unused-vars
3761                 exported = function isMap(x) {
3762                         // `Map` does not have a `has` method
3763                         return false;
3764                 };
3765         }
3766
3767         var isMap$1 = exported || function isMap(x) {
3768                 if (!x || typeof x !== 'object') {
3769                         return false;
3770                 }
3771                 try {
3772                         $mapHas.call(x);
3773                         if ($setHas) {
3774                                 try {
3775                                         $setHas.call(x);
3776                                 } catch (e) {
3777                                         return true;
3778                                 }
3779                         }
3780                         return x instanceof $Map; // core-js workaround, pre-v2.5.0
3781                 } catch (e$1) {}
3782                 return false;
3783         };
3784
3785         var $Map$1 = typeof Map === 'function' && Map.prototype ? Map : null;
3786         var $Set$1 = typeof Set === 'function' && Set.prototype ? Set : null;
3787
3788         var exported$1;
3789
3790         if (!$Set$1) {
3791                 // eslint-disable-next-line no-unused-vars
3792                 exported$1 = function isSet(x) {
3793                         // `Set` is not present in this environment.
3794                         return false;
3795                 };
3796         }
3797
3798         var $mapHas$1 = $Map$1 ? Map.prototype.has : null;
3799         var $setHas$1 = $Set$1 ? Set.prototype.has : null;
3800         if (!exported$1 && !$setHas$1) {
3801                 // eslint-disable-next-line no-unused-vars
3802                 exported$1 = function isSet(x) {
3803                         // `Set` does not have a `has` method
3804                         return false;
3805                 };
3806         }
3807
3808         var isSet$1 = exported$1 || function isSet(x) {
3809                 if (!x || typeof x !== 'object') {
3810                         return false;
3811                 }
3812                 try {
3813                         $setHas$1.call(x);
3814                         if ($mapHas$1) {
3815                                 try {
3816                                         $mapHas$1.call(x);
3817                                 } catch (e) {
3818                                         return true;
3819                                 }
3820                         }
3821                         return x instanceof $Set$1; // core-js workaround, pre-v2.5.0
3822                 } catch (e$1) {}
3823                 return false;
3824         };
3825
3826         var esGetIterator = createCommonjsModule(function (module) {
3827
3828         /* eslint global-require: 0 */
3829         // the code is structured this way so that bundlers can
3830         // alias out `has-symbols` to `() => true` or `() => false` if your target
3831         // environments' Symbol capabilities are known, and then use
3832         // dead code elimination on the rest of this module.
3833         //
3834         // Similarly, `isarray` can be aliased to `Array.isArray` if
3835         // available in all target environments.
3836
3837
3838
3839         if (hasSymbols$1() || shams()) {
3840                 var $iterator = Symbol.iterator;
3841                 // Symbol is available natively or shammed
3842                 // natively:
3843                 //  - Chrome >= 38
3844                 //  - Edge 12-14?, Edge >= 15 for sure
3845                 //  - FF >= 36
3846                 //  - Safari >= 9
3847                 //  - node >= 0.12
3848                 module.exports = function getIterator(iterable) {
3849                         // alternatively, `iterable[$iterator]?.()`
3850                         if (iterable != null && typeof iterable[$iterator] !== 'undefined') {
3851                                 return iterable[$iterator]();
3852                         }
3853                         if (isArguments$2(iterable)) {
3854                                 // arguments objects lack Symbol.iterator
3855                                 // - node 0.12
3856                                 return Array.prototype[$iterator].call(iterable);
3857                         }
3858                 };
3859         } else {
3860                 // Symbol is not available, native or shammed
3861                 var isArray = isarray;
3862                 var isString = isString$2;
3863                 var GetIntrinsic$1 = GetIntrinsic;
3864                 var $Map = GetIntrinsic$1('%Map%', true);
3865                 var $Set = GetIntrinsic$1('%Set%', true);
3866                 var callBound$1 = callBound;
3867                 var $arrayPush = callBound$1('Array.prototype.push');
3868                 var $charCodeAt = callBound$1('String.prototype.charCodeAt');
3869                 var $stringSlice = callBound$1('String.prototype.slice');
3870
3871                 var advanceStringIndex = function advanceStringIndex(S, index) {
3872                         var length = S.length;
3873                         if ((index + 1) >= length) {
3874                                 return index + 1;
3875                         }
3876
3877                         var first = $charCodeAt(S, index);
3878                         if (first < 0xD800 || first > 0xDBFF) {
3879                                 return index + 1;
3880                         }
3881
3882                         var second = $charCodeAt(S, index + 1);
3883                         if (second < 0xDC00 || second > 0xDFFF) {
3884                                 return index + 1;
3885                         }
3886
3887                         return index + 2;
3888                 };
3889
3890                 var getArrayIterator = function getArrayIterator(arraylike) {
3891                         var i = 0;
3892                         return {
3893                                 next: function next() {
3894                                         var done = i >= arraylike.length;
3895                                         var value;
3896                                         if (!done) {
3897                                                 value = arraylike[i];
3898                                                 i += 1;
3899                                         }
3900                                         return {
3901                                                 done: done,
3902                                                 value: value
3903                                         };
3904                                 }
3905                         };
3906                 };
3907
3908                 var getNonCollectionIterator = function getNonCollectionIterator(iterable) {
3909                         if (isArray(iterable) || isArguments$2(iterable)) {
3910                                 return getArrayIterator(iterable);
3911                         }
3912                         if (isString(iterable)) {
3913                                 var i = 0;
3914                                 return {
3915                                         next: function next() {
3916                                                 var nextIndex = advanceStringIndex(iterable, i);
3917                                                 var value = $stringSlice(iterable, i, nextIndex);
3918                                                 i = nextIndex;
3919                                                 return {
3920                                                         done: nextIndex > iterable.length,
3921                                                         value: value
3922                                                 };
3923                                         }
3924                                 };
3925                         }
3926                 };
3927
3928                 if (!$Map && !$Set) {
3929                         // the only language iterables are Array, String, arguments
3930                         // - Safari <= 6.0
3931                         // - Chrome < 38
3932                         // - node < 0.12
3933                         // - FF < 13
3934                         // - IE < 11
3935                         // - Edge < 11
3936
3937                         module.exports = getNonCollectionIterator;
3938                 } else {
3939                         // either Map or Set are available, but Symbol is not
3940                         // - es6-shim on an ES5 browser
3941                         // - Safari 6.2 (maybe 6.1?)
3942                         // - FF v[13, 36)
3943                         // - IE 11
3944                         // - Edge 11
3945                         // - Safari v[6, 9)
3946
3947                         var isMap = isMap$1;
3948                         var isSet = isSet$1;
3949
3950                         // Firefox >= 27, IE 11, Safari 6.2 - 9, Edge 11, es6-shim in older envs, all have forEach
3951                         var $mapForEach = callBound$1('Map.prototype.forEach', true);
3952                         var $setForEach = callBound$1('Set.prototype.forEach', true);
3953                         if (typeof process === 'undefined' || !process.versions || !process.versions.node) { // "if is not node"
3954
3955                                 // Firefox 17 - 26 has `.iterator()`, whose iterator `.next()` either
3956                                 // returns a value, or throws a StopIteration object. These browsers
3957                                 // do not have any other mechanism for iteration.
3958                                 var $mapIterator = callBound$1('Map.prototype.iterator', true);
3959                                 var $setIterator = callBound$1('Set.prototype.iterator', true);
3960                                 var getStopIterationIterator = function (iterator) {
3961                                         var done = false;
3962                                         return {
3963                                                 next: function next() {
3964                                                         try {
3965                                                                 return {
3966                                                                         done: done,
3967                                                                         value: done ? undefined : iterator.next()
3968                                                                 };
3969                                                         } catch (e) {
3970                                                                 done = true;
3971                                                                 return {
3972                                                                         done: true,
3973                                                                         value: undefined
3974                                                                 };
3975                                                         }
3976                                                 }
3977                                         };
3978                                 };
3979                         }
3980                         // Firefox 27-35, and some older es6-shim versions, use a string "@@iterator" property
3981                         // this returns a proper iterator object, so we should use it instead of forEach.
3982                         // newer es6-shim versions use a string "_es6-shim iterator_" property.
3983                         var $mapAtAtIterator = callBound$1('Map.prototype.@@iterator', true) || callBound$1('Map.prototype._es6-shim iterator_', true);
3984                         var $setAtAtIterator = callBound$1('Set.prototype.@@iterator', true) || callBound$1('Set.prototype._es6-shim iterator_', true);
3985
3986                         var getCollectionIterator = function getCollectionIterator(iterable) {
3987                                 if (isMap(iterable)) {
3988                                         if ($mapIterator) {
3989                                                 return getStopIterationIterator($mapIterator(iterable));
3990                                         }
3991                                         if ($mapAtAtIterator) {
3992                                                 return $mapAtAtIterator(iterable);
3993                                         }
3994                                         if ($mapForEach) {
3995                                                 var entries = [];
3996                                                 $mapForEach(iterable, function (v, k) {
3997                                                         $arrayPush(entries, [k, v]);
3998                                                 });
3999                                                 return getArrayIterator(entries);
4000                                         }
4001                                 }
4002                                 if (isSet(iterable)) {
4003                                         if ($setIterator) {
4004                                                 return getStopIterationIterator($setIterator(iterable));
4005                                         }
4006                                         if ($setAtAtIterator) {
4007                                                 return $setAtAtIterator(iterable);
4008                                         }
4009                                         if ($setForEach) {
4010                                                 var values = [];
4011                                                 $setForEach(iterable, function (v) {
4012                                                         $arrayPush(values, v);
4013                                                 });
4014                                                 return getArrayIterator(values);
4015                                         }
4016                                 }
4017                         };
4018
4019                         module.exports = function getIterator(iterable) {
4020                                 return getCollectionIterator(iterable) || getNonCollectionIterator(iterable);
4021                         };
4022                 }
4023         }
4024         });
4025
4026         var $TypeError$c = TypeError;
4027
4028         // eslint-disable-next-line consistent-return
4029         var iterateIterator = function iterateIterator(iterator) {
4030                 if (!iterator || typeof iterator.next !== 'function') {
4031                         throw new $TypeError$c('iterator must be an object with a `next` method');
4032                 }
4033                 if (arguments.length > 1) {
4034                         var callback = arguments[1];
4035                         if (typeof callback !== 'function') {
4036                                 throw new $TypeError$c('`callback`, if provided, must be a function');
4037                         }
4038                 }
4039                 var values = callback || [];
4040                 var result;
4041                 while ((result = iterator.next()) && !result.done) {
4042                         if (callback) {
4043                                 callback(result.value); // eslint-disable-line callback-return
4044                         } else {
4045                                 values.push(result.value);
4046                         }
4047                 }
4048                 if (!callback) {
4049                         return values;
4050                 }
4051         };
4052
4053         var $TypeError$d = TypeError;
4054
4055
4056         var iterateValue = function iterateValue(iterable) {
4057                 var iterator = esGetIterator(iterable);
4058                 if (!iterator) {
4059                         throw new $TypeError$d('non-iterable value provided');
4060                 }
4061                 if (arguments.length > 1) {
4062                         return iterateIterator(iterator, arguments[1]);
4063                 }
4064                 return iterateIterator(iterator);
4065         };
4066
4067         var implementation$5 = function from(items) {
4068                 var C = this;
4069                 if (items === null || typeof items === 'undefined') {
4070                         throw new TypeError('`Array.from` requires an array-like object, not `null` or `undefined`');
4071                 }
4072                 var mapFn, T;
4073                 if (typeof arguments[1] !== 'undefined') {
4074                         mapFn = arguments[1];
4075                         if (!IsCallable(mapFn)) {
4076                                 throw new TypeError('When provided, the second argument to `Array.from` must be a function');
4077                         }
4078                         if (arguments.length > 2) {
4079                                 T = arguments[2];
4080                         }
4081                 }
4082
4083                 var values;
4084                 try {
4085                         values = iterateValue(items);
4086                 } catch (e) {
4087                         values = items;
4088                 }
4089
4090                 var arrayLike = ToObject(values);
4091                 var len = ToLength(arrayLike.length);
4092                 var A = IsConstructor(C) ? ToObject(new C(len)) : new Array(len);
4093                 var k = 0;
4094                 var kValue, mappedValue;
4095
4096                 while (k < len) {
4097                         var Pk = ToString(k);
4098                         kValue = Get(arrayLike, Pk);
4099                         if (mapFn) {
4100                                 mappedValue = typeof T === 'undefined' ? mapFn(kValue, k) : Call(mapFn, T, [kValue, k]);
4101                         } else {
4102                                 mappedValue = kValue;
4103                         }
4104                         CreateDataPropertyOrThrow(A, Pk, mappedValue);
4105                         k += 1;
4106                 }
4107                 A.length = len;
4108                 return A;
4109         };
4110
4111         var tryCall = function (fn) {
4112                 try {
4113                         return fn();
4114                 } catch (e) {
4115                         return false;
4116                 }
4117         };
4118
4119         var polyfill$8 = function getPolyfill() {
4120                 if (IsCallable(Array.from)) {
4121                         var handlesUndefMapper = tryCall(function () {
4122                                 // Microsoft Edge v0.11 throws if the mapFn argument is *provided* but undefined,
4123                                 // but the spec doesn't care if it's provided or not - undefined doesn't throw.
4124                                 return Array.from([0], undefined);
4125                         });
4126                         if (!handlesUndefMapper) {
4127                                 var origArrayFrom = Array.from;
4128                                 return function from(items) {
4129                                         /* eslint no-invalid-this: 0 */
4130                                         if (arguments.length > 1 && typeof arguments[1] !== 'undefined') {
4131                                                 return Call(origArrayFrom, this, arguments);
4132                                         } else {
4133                                                 return Call(origArrayFrom, this, [items]);
4134                                         }
4135                                 };
4136                         }
4137                         var implemented = tryCall(function () {
4138                                 // Detects a Firefox bug in v32
4139                                 // https://bugzilla.mozilla.org/show_bug.cgi?id=1063993
4140                                 return Array.from({ 'length': -1 }) === 0;
4141                         })
4142                         && tryCall(function () {
4143                                 // Detects a bug in Webkit nightly r181886
4144                                 var arr = Array.from([0].entries());
4145                                 return arr.length === 1 && IsArray(arr[0]) && arr[0][0] === 0 && arr[0][1] === 0;
4146                         })
4147                         && tryCall(function () {
4148                                 return Array.from({ 'length': -Infinity });
4149                         });
4150                         if (implemented) {
4151                                 return Array.from;
4152                         }
4153                 }
4154
4155                 return implementation$5;
4156         };
4157
4158         var shim$a = function shimArrayFrom() {
4159                 var polyfill = polyfill$8();
4160
4161                 defineProperties_1(Array, { 'from': polyfill }, {
4162                         'from': function () {
4163                                 return Array.from !== polyfill;
4164                         }
4165                 });
4166
4167                 return polyfill;
4168         };
4169
4170         var polyfill$9 = polyfill$8();
4171
4172         // eslint-disable-next-line no-unused-vars
4173         var boundFromShim = function from(items) {
4174                 // eslint-disable-next-line no-invalid-this
4175                 return polyfill$9.apply(this || Array, arguments);
4176         };
4177
4178         defineProperties_1(boundFromShim, {
4179                 'getPolyfill': polyfill$8,
4180                 'implementation': implementation$5,
4181                 'shim': shim$a
4182         });
4183
4184         var array_from = boundFromShim;
4185
4186         var $isEnumerable$2 = callBound('Object.prototype.propertyIsEnumerable');
4187
4188         var implementation$6 = function values(O) {
4189                 var obj = RequireObjectCoercible(O);
4190                 var vals = [];
4191                 for (var key in obj) {
4192                         if (src(obj, key) && $isEnumerable$2(obj, key)) {
4193                                 vals.push(obj[key]);
4194                         }
4195                 }
4196                 return vals;
4197         };
4198
4199         var polyfill$a = function getPolyfill() {
4200                 return typeof Object.values === 'function' ? Object.values : implementation$6;
4201         };
4202
4203         var shim$b = function shimValues() {
4204                 var polyfill = polyfill$a();
4205                 defineProperties_1(Object, { values: polyfill }, {
4206                         values: function testValues() {
4207                                 return Object.values !== polyfill;
4208                         }
4209                 });
4210                 return polyfill;
4211         };
4212
4213         var polyfill$b = polyfill$a();
4214
4215         defineProperties_1(polyfill$b, {
4216                 getPolyfill: polyfill$a,
4217                 implementation: implementation$6,
4218                 shim: shim$b
4219         });
4220
4221         var object_values = polyfill$b;
4222
4223         // modified from https://github.com/es-shims/es6-shim
4224
4225
4226         var canBeObject = function (obj) {
4227                 return typeof obj !== 'undefined' && obj !== null;
4228         };
4229         var hasSymbols$5 = shams();
4230         var toObject = Object;
4231         var push = functionBind.call(Function.call, Array.prototype.push);
4232         var propIsEnumerable = functionBind.call(Function.call, Object.prototype.propertyIsEnumerable);
4233         var originalGetSymbols = hasSymbols$5 ? Object.getOwnPropertySymbols : null;
4234
4235         var implementation$7 = function assign(target, source1) {
4236                 var arguments$1 = arguments;
4237
4238                 if (!canBeObject(target)) { throw new TypeError('target must be an object'); }
4239                 var objTarget = toObject(target);
4240                 var s, source, i, props, syms, value, key;
4241                 for (s = 1; s < arguments.length; ++s) {
4242                         source = toObject(arguments$1[s]);
4243                         props = objectKeys(source);
4244                         var getSymbols = hasSymbols$5 && (Object.getOwnPropertySymbols || originalGetSymbols);
4245                         if (getSymbols) {
4246                                 syms = getSymbols(source);
4247                                 for (i = 0; i < syms.length; ++i) {
4248                                         key = syms[i];
4249                                         if (propIsEnumerable(source, key)) {
4250                                                 push(props, key);
4251                                         }
4252                                 }
4253                         }
4254                         for (i = 0; i < props.length; ++i) {
4255                                 key = props[i];
4256                                 value = source[key];
4257                                 if (propIsEnumerable(source, key)) {
4258                                         objTarget[key] = value;
4259                                 }
4260                         }
4261                 }
4262                 return objTarget;
4263         };
4264
4265         var lacksProperEnumerationOrder = function () {
4266                 if (!Object.assign) {
4267                         return false;
4268                 }
4269                 // v8, specifically in node 4.x, has a bug with incorrect property enumeration order
4270                 // note: this does not detect the bug unless there's 20 characters
4271                 var str = 'abcdefghijklmnopqrst';
4272                 var letters = str.split('');
4273                 var map = {};
4274                 for (var i = 0; i < letters.length; ++i) {
4275                         map[letters[i]] = letters[i];
4276                 }
4277                 var obj = Object.assign({}, map);
4278                 var actual = '';
4279                 for (var k in obj) {
4280                         actual += k;
4281                 }
4282                 return str !== actual;
4283         };
4284
4285         var assignHasPendingExceptions = function () {
4286                 if (!Object.assign || !Object.preventExtensions) {
4287                         return false;
4288                 }
4289                 // Firefox 37 still has "pending exception" logic in its Object.assign implementation,
4290                 // which is 72% slower than our shim, and Firefox 40's native implementation.
4291                 var thrower = Object.preventExtensions({ 1: 2 });
4292                 try {
4293                         Object.assign(thrower, 'xy');
4294                 } catch (e) {
4295                         return thrower[1] === 'y';
4296                 }
4297                 return false;
4298         };
4299
4300         var polyfill$c = function getPolyfill() {
4301                 if (!Object.assign) {
4302                         return implementation$7;
4303                 }
4304                 if (lacksProperEnumerationOrder()) {
4305                         return implementation$7;
4306                 }
4307                 if (assignHasPendingExceptions()) {
4308                         return implementation$7;
4309                 }
4310                 return Object.assign;
4311         };
4312
4313         var shim$c = function shimAssign() {
4314                 var polyfill = polyfill$c();
4315                 defineProperties_1(
4316                         Object,
4317                         { assign: polyfill },
4318                         { assign: function () { return Object.assign !== polyfill; } }
4319                 );
4320                 return polyfill;
4321         };
4322
4323         var polyfill$d = polyfill$c();
4324
4325         defineProperties_1(polyfill$d, {
4326                 getPolyfill: polyfill$c,
4327                 implementation: implementation$7,
4328                 shim: shim$c
4329         });
4330
4331         var object_assign = polyfill$d;
4332
4333         /**
4334          * @this {Promise}
4335          */
4336         function finallyConstructor(callback) {
4337           var constructor = this.constructor;
4338           return this.then(
4339             function(value) {
4340               // @ts-ignore
4341               return constructor.resolve(callback()).then(function() {
4342                 return value;
4343               });
4344             },
4345             function(reason) {
4346               // @ts-ignore
4347               return constructor.resolve(callback()).then(function() {
4348                 // @ts-ignore
4349                 return constructor.reject(reason);
4350               });
4351             }
4352           );
4353         }
4354
4355         // Store setTimeout reference so promise-polyfill will be unaffected by
4356         // other code modifying setTimeout (like sinon.useFakeTimers())
4357         var setTimeoutFunc = setTimeout;
4358
4359         function isArray$4(x) {
4360           return Boolean(x && typeof x.length !== 'undefined');
4361         }
4362
4363         function noop$1() {}
4364
4365         // Polyfill for Function.prototype.bind
4366         function bind$2(fn, thisArg) {
4367           return function() {
4368             fn.apply(thisArg, arguments);
4369           };
4370         }
4371
4372         /**
4373          * @constructor
4374          * @param {Function} fn
4375          */
4376         function Promise$1(fn) {
4377           if (!(this instanceof Promise$1))
4378             { throw new TypeError('Promises must be constructed via new'); }
4379           if (typeof fn !== 'function') { throw new TypeError('not a function'); }
4380           /** @type {!number} */
4381           this._state = 0;
4382           /** @type {!boolean} */
4383           this._handled = false;
4384           /** @type {Promise|undefined} */
4385           this._value = undefined;
4386           /** @type {!Array<!Function>} */
4387           this._deferreds = [];
4388
4389           doResolve(fn, this);
4390         }
4391
4392         function handle(self, deferred) {
4393           while (self._state === 3) {
4394             self = self._value;
4395           }
4396           if (self._state === 0) {
4397             self._deferreds.push(deferred);
4398             return;
4399           }
4400           self._handled = true;
4401           Promise$1._immediateFn(function() {
4402             var cb = self._state === 1 ? deferred.onFulfilled : deferred.onRejected;
4403             if (cb === null) {
4404               (self._state === 1 ? resolve : reject)(deferred.promise, self._value);
4405               return;
4406             }
4407             var ret;
4408             try {
4409               ret = cb(self._value);
4410             } catch (e) {
4411               reject(deferred.promise, e);
4412               return;
4413             }
4414             resolve(deferred.promise, ret);
4415           });
4416         }
4417
4418         function resolve(self, newValue) {
4419           try {
4420             // Promise Resolution Procedure: https://github.com/promises-aplus/promises-spec#the-promise-resolution-procedure
4421             if (newValue === self)
4422               { throw new TypeError('A promise cannot be resolved with itself.'); }
4423             if (
4424               newValue &&
4425               (typeof newValue === 'object' || typeof newValue === 'function')
4426             ) {
4427               var then = newValue.then;
4428               if (newValue instanceof Promise$1) {
4429                 self._state = 3;
4430                 self._value = newValue;
4431                 finale(self);
4432                 return;
4433               } else if (typeof then === 'function') {
4434                 doResolve(bind$2(then, newValue), self);
4435                 return;
4436               }
4437             }
4438             self._state = 1;
4439             self._value = newValue;
4440             finale(self);
4441           } catch (e) {
4442             reject(self, e);
4443           }
4444         }
4445
4446         function reject(self, newValue) {
4447           self._state = 2;
4448           self._value = newValue;
4449           finale(self);
4450         }
4451
4452         function finale(self) {
4453           if (self._state === 2 && self._deferreds.length === 0) {
4454             Promise$1._immediateFn(function() {
4455               if (!self._handled) {
4456                 Promise$1._unhandledRejectionFn(self._value);
4457               }
4458             });
4459           }
4460
4461           for (var i = 0, len = self._deferreds.length; i < len; i++) {
4462             handle(self, self._deferreds[i]);
4463           }
4464           self._deferreds = null;
4465         }
4466
4467         /**
4468          * @constructor
4469          */
4470         function Handler(onFulfilled, onRejected, promise) {
4471           this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null;
4472           this.onRejected = typeof onRejected === 'function' ? onRejected : null;
4473           this.promise = promise;
4474         }
4475
4476         /**
4477          * Take a potentially misbehaving resolver function and make sure
4478          * onFulfilled and onRejected are only called once.
4479          *
4480          * Makes no guarantees about asynchrony.
4481          */
4482         function doResolve(fn, self) {
4483           var done = false;
4484           try {
4485             fn(
4486               function(value) {
4487                 if (done) { return; }
4488                 done = true;
4489                 resolve(self, value);
4490               },
4491               function(reason) {
4492                 if (done) { return; }
4493                 done = true;
4494                 reject(self, reason);
4495               }
4496             );
4497           } catch (ex) {
4498             if (done) { return; }
4499             done = true;
4500             reject(self, ex);
4501           }
4502         }
4503
4504         Promise$1.prototype['catch'] = function(onRejected) {
4505           return this.then(null, onRejected);
4506         };
4507
4508         Promise$1.prototype.then = function(onFulfilled, onRejected) {
4509           // @ts-ignore
4510           var prom = new this.constructor(noop$1);
4511
4512           handle(this, new Handler(onFulfilled, onRejected, prom));
4513           return prom;
4514         };
4515
4516         Promise$1.prototype['finally'] = finallyConstructor;
4517
4518         Promise$1.all = function(arr) {
4519           return new Promise$1(function(resolve, reject) {
4520             if (!isArray$4(arr)) {
4521               return reject(new TypeError('Promise.all accepts an array'));
4522             }
4523
4524             var args = Array.prototype.slice.call(arr);
4525             if (args.length === 0) { return resolve([]); }
4526             var remaining = args.length;
4527
4528             function res(i, val) {
4529               try {
4530                 if (val && (typeof val === 'object' || typeof val === 'function')) {
4531                   var then = val.then;
4532                   if (typeof then === 'function') {
4533                     then.call(
4534                       val,
4535                       function(val) {
4536                         res(i, val);
4537                       },
4538                       reject
4539                     );
4540                     return;
4541                   }
4542                 }
4543                 args[i] = val;
4544                 if (--remaining === 0) {
4545                   resolve(args);
4546                 }
4547               } catch (ex) {
4548                 reject(ex);
4549               }
4550             }
4551
4552             for (var i = 0; i < args.length; i++) {
4553               res(i, args[i]);
4554             }
4555           });
4556         };
4557
4558         Promise$1.resolve = function(value) {
4559           if (value && typeof value === 'object' && value.constructor === Promise$1) {
4560             return value;
4561           }
4562
4563           return new Promise$1(function(resolve) {
4564             resolve(value);
4565           });
4566         };
4567
4568         Promise$1.reject = function(value) {
4569           return new Promise$1(function(resolve, reject) {
4570             reject(value);
4571           });
4572         };
4573
4574         Promise$1.race = function(arr) {
4575           return new Promise$1(function(resolve, reject) {
4576             if (!isArray$4(arr)) {
4577               return reject(new TypeError('Promise.race accepts an array'));
4578             }
4579
4580             for (var i = 0, len = arr.length; i < len; i++) {
4581               Promise$1.resolve(arr[i]).then(resolve, reject);
4582             }
4583           });
4584         };
4585
4586         // Use polyfill for setImmediate for performance gains
4587         Promise$1._immediateFn =
4588           // @ts-ignore
4589           (typeof setImmediate === 'function' &&
4590             function(fn) {
4591               // @ts-ignore
4592               setImmediate(fn);
4593             }) ||
4594           function(fn) {
4595             setTimeoutFunc(fn, 0);
4596           };
4597
4598         Promise$1._unhandledRejectionFn = function _unhandledRejectionFn(err) {
4599           if (typeof console !== 'undefined' && console) {
4600             console.warn('Possible Unhandled Promise Rejection:', err); // eslint-disable-line no-console
4601           }
4602         };
4603
4604         /** @suppress {undefinedVars} */
4605         var globalNS = (function() {
4606           // the only reliable means to get the global object is
4607           // `Function('return this')()`
4608           // However, this causes CSP violations in Chrome apps.
4609           if (typeof self !== 'undefined') {
4610             return self;
4611           }
4612           if (typeof window !== 'undefined') {
4613             return window;
4614           }
4615           if (typeof global !== 'undefined') {
4616             return global;
4617           }
4618           throw new Error('unable to locate global object');
4619         })();
4620
4621         if (!('Promise' in globalNS)) {
4622           globalNS['Promise'] = Promise$1;
4623         } else if (!globalNS.Promise.prototype['finally']) {
4624           globalNS.Promise.prototype['finally'] = finallyConstructor;
4625         }
4626
4627         var polyfill$e = /*#__PURE__*/Object.freeze({
4628                 __proto__: null
4629         });
4630
4631         var setAsap = createCommonjsModule(function (module) {
4632         (function (thisVar, undefined$1) {
4633                 var main = (typeof window === 'object' && window) || (typeof commonjsGlobal === 'object' && commonjsGlobal) ||
4634                         typeof self === 'object' && self || thisVar;
4635
4636                 var hasSetImmediate = typeof setImmediate === 'function';
4637                 var hasNextTick = typeof process === 'object' && !!process && typeof process.nextTick === 'function';
4638                 var index = 0;
4639
4640                 function getNewIndex() {
4641                         if (index === 9007199254740991) {
4642                                 return 0;
4643                         }
4644                         return ++index;
4645                 }
4646
4647                 var setAsap = (function () {
4648                         var hiddenDiv, scriptEl, timeoutFn, callbacks;
4649
4650                         // Modern browsers, fastest async
4651                         if (main.MutationObserver) {
4652                                 return function setAsap(callback) {
4653                                         hiddenDiv = document.createElement("div");
4654                                         (new MutationObserver(function() {
4655                                                 callback();
4656                                                 hiddenDiv = null;
4657                                         })).observe(hiddenDiv, { attributes: true });
4658                                         hiddenDiv.setAttribute('i', '1');
4659                                 };
4660
4661                         // Browsers that support postMessage
4662                         } else if (!hasSetImmediate && main.postMessage && !main.importScripts && main.addEventListener) {
4663
4664                                 var MESSAGE_PREFIX = "com.setImmediate" + Math.random();
4665                                 callbacks = {};
4666
4667                                 var onGlobalMessage = function (event) {
4668                                         if (event.source === main && event.data.indexOf(MESSAGE_PREFIX) === 0) {
4669                                                 var i = +event.data.split(':')[1];
4670                                                 callbacks[i]();
4671                                                 delete callbacks[i];
4672                                         }
4673                                 };
4674
4675                                 main.addEventListener("message", onGlobalMessage, false);
4676
4677                                 return function setAsap(callback) {
4678                                         var i = getNewIndex();
4679                                         callbacks[i] = callback;
4680                                         main.postMessage(MESSAGE_PREFIX + ':' + i, "*");
4681                                 };
4682
4683                                 // IE browsers without postMessage
4684                         } else if (!hasSetImmediate && main.document && 'onreadystatechange' in document.createElement('script')) {
4685
4686                                 return function setAsap(callback) {
4687                                         scriptEl = document.createElement("script");
4688                                         scriptEl.onreadystatechange = function onreadystatechange() {
4689                                                 scriptEl.onreadystatechange = null;
4690                                                 scriptEl.parentNode.removeChild(scriptEl);
4691                                                 scriptEl = null;
4692                                                 callback();
4693                                         };
4694                                         document.body.appendChild(scriptEl);
4695                                 };
4696
4697                         // All other browsers and node
4698                         } else {
4699
4700                                 timeoutFn = (hasSetImmediate && setImmediate) || (hasNextTick && process.nextTick) || setTimeout;
4701                                 return function setAsap(callback) {
4702                                         timeoutFn(callback);
4703                                 };
4704                         }
4705
4706                 })();
4707
4708                 if ( module.exports) {
4709                         module.exports = setAsap;
4710                 } else if (typeof commonjsRequire !== 'undefined' && commonjsRequire.amd) {
4711                         undefined$1(function () {
4712                                 return setAsap;
4713                         });
4714                 } else {
4715                         main.setAsap = setAsap;
4716                 }
4717         })(commonjsGlobal);
4718         });
4719
4720         var performanceNow = createCommonjsModule(function (module) {
4721         // Generated by CoffeeScript 1.12.2
4722         (function() {
4723           var getNanoSeconds, hrtime, loadTime, moduleLoadTime, nodeLoadTime, upTime;
4724
4725           if ((typeof performance !== "undefined" && performance !== null) && performance.now) {
4726             module.exports = function() {
4727               return performance.now();
4728             };
4729           } else if ((typeof process !== "undefined" && process !== null) && process.hrtime) {
4730             module.exports = function() {
4731               return (getNanoSeconds() - nodeLoadTime) / 1e6;
4732             };
4733             hrtime = process.hrtime;
4734             getNanoSeconds = function() {
4735               var hr;
4736               hr = hrtime();
4737               return hr[0] * 1e9 + hr[1];
4738             };
4739             moduleLoadTime = getNanoSeconds();
4740             upTime = process.uptime() * 1e9;
4741             nodeLoadTime = moduleLoadTime - upTime;
4742           } else if (Date.now) {
4743             module.exports = function() {
4744               return Date.now() - loadTime;
4745             };
4746             loadTime = Date.now();
4747           } else {
4748             module.exports = function() {
4749               return new Date().getTime() - loadTime;
4750             };
4751             loadTime = new Date().getTime();
4752           }
4753
4754         }).call(commonjsGlobal);
4755
4756
4757         });
4758
4759         var root = typeof window === 'undefined' ? commonjsGlobal : window
4760           , vendors = ['moz', 'webkit']
4761           , suffix = 'AnimationFrame'
4762           , raf = root['request' + suffix]
4763           , caf = root['cancel' + suffix] || root['cancelRequest' + suffix];
4764
4765         for(var i = 0; !raf && i < vendors.length; i++) {
4766           raf = root[vendors[i] + 'Request' + suffix];
4767           caf = root[vendors[i] + 'Cancel' + suffix]
4768               || root[vendors[i] + 'CancelRequest' + suffix];
4769         }
4770
4771         // Some versions of FF have rAF but not cAF
4772         if(!raf || !caf) {
4773           var last = 0
4774             , id$2 = 0
4775             , queue = []
4776             , frameDuration = 1000 / 60;
4777
4778           raf = function(callback) {
4779             if(queue.length === 0) {
4780               var _now = performanceNow()
4781                 , next = Math.max(0, frameDuration - (_now - last));
4782               last = next + _now;
4783               setTimeout(function() {
4784                 var cp = queue.slice(0);
4785                 // Clear queue here to prevent
4786                 // callbacks from appending listeners
4787                 // to the current frame's queue
4788                 queue.length = 0;
4789                 for(var i = 0; i < cp.length; i++) {
4790                   if(!cp[i].cancelled) {
4791                     try{
4792                       cp[i].callback(last);
4793                     } catch(e) {
4794                       setTimeout(function() { throw e }, 0);
4795                     }
4796                   }
4797                 }
4798               }, Math.round(next));
4799             }
4800             queue.push({
4801               handle: ++id$2,
4802               callback: callback,
4803               cancelled: false
4804             });
4805             return id$2
4806           };
4807
4808           caf = function(handle) {
4809             for(var i = 0; i < queue.length; i++) {
4810               if(queue[i].handle === handle) {
4811                 queue[i].cancelled = true;
4812               }
4813             }
4814           };
4815         }
4816
4817         var raf_1 = function(fn) {
4818           // Wrap in a new function to prevent
4819           // `cancel` potentially being assigned
4820           // to the native rAF function
4821           return raf.call(root, fn)
4822         };
4823         var cancel = function() {
4824           caf.apply(root, arguments);
4825         };
4826         var polyfill$f = function(object) {
4827           if (!object) {
4828             object = root;
4829           }
4830           object.requestAnimationFrame = raf;
4831           object.cancelAnimationFrame = caf;
4832         };
4833         raf_1.cancel = cancel;
4834         raf_1.polyfill = polyfill$f;
4835
4836         var global$1 = (function(self) {
4837           return self
4838           // eslint-disable-next-line no-invalid-this
4839         })(typeof self !== 'undefined' ? self : undefined);
4840         var support = {
4841           searchParams: 'URLSearchParams' in global$1,
4842           iterable: 'Symbol' in global$1 && 'iterator' in Symbol,
4843           blob:
4844             'FileReader' in global$1 &&
4845             'Blob' in global$1 &&
4846             (function() {
4847               try {
4848                 new Blob();
4849                 return true
4850               } catch (e) {
4851                 return false
4852               }
4853             })(),
4854           formData: 'FormData' in global$1,
4855           arrayBuffer: 'ArrayBuffer' in global$1
4856         };
4857
4858         function isDataView(obj) {
4859           return obj && DataView.prototype.isPrototypeOf(obj)
4860         }
4861
4862         if (support.arrayBuffer) {
4863           var viewClasses = [
4864             '[object Int8Array]',
4865             '[object Uint8Array]',
4866             '[object Uint8ClampedArray]',
4867             '[object Int16Array]',
4868             '[object Uint16Array]',
4869             '[object Int32Array]',
4870             '[object Uint32Array]',
4871             '[object Float32Array]',
4872             '[object Float64Array]'
4873           ];
4874
4875           var isArrayBufferView =
4876             ArrayBuffer.isView ||
4877             function(obj) {
4878               return obj && viewClasses.indexOf(Object.prototype.toString.call(obj)) > -1
4879             };
4880         }
4881
4882         function normalizeName(name) {
4883           if (typeof name !== 'string') {
4884             name = String(name);
4885           }
4886           if (/[^a-z0-9\-#$%&'*+.^_`|~!]/i.test(name) || name === '') {
4887             throw new TypeError('Invalid character in header field name')
4888           }
4889           return name.toLowerCase()
4890         }
4891
4892         function normalizeValue(value) {
4893           if (typeof value !== 'string') {
4894             value = String(value);
4895           }
4896           return value
4897         }
4898
4899         // Build a destructive iterator for the value list
4900         function iteratorFor(items) {
4901           var iterator = {
4902             next: function() {
4903               var value = items.shift();
4904               return {done: value === undefined, value: value}
4905             }
4906           };
4907
4908           if (support.iterable) {
4909             iterator[Symbol.iterator] = function() {
4910               return iterator
4911             };
4912           }
4913
4914           return iterator
4915         }
4916
4917         function Headers(headers) {
4918           this.map = {};
4919
4920           if (headers instanceof Headers) {
4921             headers.forEach(function(value, name) {
4922               this.append(name, value);
4923             }, this);
4924           } else if (Array.isArray(headers)) {
4925             headers.forEach(function(header) {
4926               this.append(header[0], header[1]);
4927             }, this);
4928           } else if (headers) {
4929             Object.getOwnPropertyNames(headers).forEach(function(name) {
4930               this.append(name, headers[name]);
4931             }, this);
4932           }
4933         }
4934
4935         Headers.prototype.append = function(name, value) {
4936           name = normalizeName(name);
4937           value = normalizeValue(value);
4938           var oldValue = this.map[name];
4939           this.map[name] = oldValue ? oldValue + ', ' + value : value;
4940         };
4941
4942         Headers.prototype['delete'] = function(name) {
4943           delete this.map[normalizeName(name)];
4944         };
4945
4946         Headers.prototype.get = function(name) {
4947           name = normalizeName(name);
4948           return this.has(name) ? this.map[name] : null
4949         };
4950
4951         Headers.prototype.has = function(name) {
4952           return this.map.hasOwnProperty(normalizeName(name))
4953         };
4954
4955         Headers.prototype.set = function(name, value) {
4956           this.map[normalizeName(name)] = normalizeValue(value);
4957         };
4958
4959         Headers.prototype.forEach = function(callback, thisArg) {
4960           for (var name in this.map) {
4961             if (this.map.hasOwnProperty(name)) {
4962               callback.call(thisArg, this.map[name], name, this);
4963             }
4964           }
4965         };
4966
4967         Headers.prototype.keys = function() {
4968           var items = [];
4969           this.forEach(function(value, name) {
4970             items.push(name);
4971           });
4972           return iteratorFor(items)
4973         };
4974
4975         Headers.prototype.values = function() {
4976           var items = [];
4977           this.forEach(function(value) {
4978             items.push(value);
4979           });
4980           return iteratorFor(items)
4981         };
4982
4983         Headers.prototype.entries = function() {
4984           var items = [];
4985           this.forEach(function(value, name) {
4986             items.push([name, value]);
4987           });
4988           return iteratorFor(items)
4989         };
4990
4991         if (support.iterable) {
4992           Headers.prototype[Symbol.iterator] = Headers.prototype.entries;
4993         }
4994
4995         function consumed(body) {
4996           if (body.bodyUsed) {
4997             return Promise.reject(new TypeError('Already read'))
4998           }
4999           body.bodyUsed = true;
5000         }
5001
5002         function fileReaderReady(reader) {
5003           return new Promise(function(resolve, reject) {
5004             reader.onload = function() {
5005               resolve(reader.result);
5006             };
5007             reader.onerror = function() {
5008               reject(reader.error);
5009             };
5010           })
5011         }
5012
5013         function readBlobAsArrayBuffer(blob) {
5014           var reader = new FileReader();
5015           var promise = fileReaderReady(reader);
5016           reader.readAsArrayBuffer(blob);
5017           return promise
5018         }
5019
5020         function readBlobAsText(blob) {
5021           var reader = new FileReader();
5022           var promise = fileReaderReady(reader);
5023           reader.readAsText(blob);
5024           return promise
5025         }
5026
5027         function readArrayBufferAsText(buf) {
5028           var view = new Uint8Array(buf);
5029           var chars = new Array(view.length);
5030
5031           for (var i = 0; i < view.length; i++) {
5032             chars[i] = String.fromCharCode(view[i]);
5033           }
5034           return chars.join('')
5035         }
5036
5037         function bufferClone(buf) {
5038           if (buf.slice) {
5039             return buf.slice(0)
5040           } else {
5041             var view = new Uint8Array(buf.byteLength);
5042             view.set(new Uint8Array(buf));
5043             return view.buffer
5044           }
5045         }
5046
5047         function Body() {
5048           this.bodyUsed = false;
5049
5050           this._initBody = function(body) {
5051             /*
5052               fetch-mock wraps the Response object in an ES6 Proxy to
5053               provide useful test harness features such as flush. However, on
5054               ES5 browsers without fetch or Proxy support pollyfills must be used;
5055               the proxy-pollyfill is unable to proxy an attribute unless it exists
5056               on the object before the Proxy is created. This change ensures
5057               Response.bodyUsed exists on the instance, while maintaining the
5058               semantic of setting Request.bodyUsed in the constructor before
5059               _initBody is called.
5060             */
5061             this.bodyUsed = this.bodyUsed;
5062             this._bodyInit = body;
5063             if (!body) {
5064               this._bodyText = '';
5065             } else if (typeof body === 'string') {
5066               this._bodyText = body;
5067             } else if (support.blob && Blob.prototype.isPrototypeOf(body)) {
5068               this._bodyBlob = body;
5069             } else if (support.formData && FormData.prototype.isPrototypeOf(body)) {
5070               this._bodyFormData = body;
5071             } else if (support.searchParams && URLSearchParams.prototype.isPrototypeOf(body)) {
5072               this._bodyText = body.toString();
5073             } else if (support.arrayBuffer && support.blob && isDataView(body)) {
5074               this._bodyArrayBuffer = bufferClone(body.buffer);
5075               // IE 10-11 can't handle a DataView body.
5076               this._bodyInit = new Blob([this._bodyArrayBuffer]);
5077             } else if (support.arrayBuffer && (ArrayBuffer.prototype.isPrototypeOf(body) || isArrayBufferView(body))) {
5078               this._bodyArrayBuffer = bufferClone(body);
5079             } else {
5080               this._bodyText = body = Object.prototype.toString.call(body);
5081             }
5082
5083             if (!this.headers.get('content-type')) {
5084               if (typeof body === 'string') {
5085                 this.headers.set('content-type', 'text/plain;charset=UTF-8');
5086               } else if (this._bodyBlob && this._bodyBlob.type) {
5087                 this.headers.set('content-type', this._bodyBlob.type);
5088               } else if (support.searchParams && URLSearchParams.prototype.isPrototypeOf(body)) {
5089                 this.headers.set('content-type', 'application/x-www-form-urlencoded;charset=UTF-8');
5090               }
5091             }
5092           };
5093
5094           if (support.blob) {
5095             this.blob = function() {
5096               var rejected = consumed(this);
5097               if (rejected) {
5098                 return rejected
5099               }
5100
5101               if (this._bodyBlob) {
5102                 return Promise.resolve(this._bodyBlob)
5103               } else if (this._bodyArrayBuffer) {
5104                 return Promise.resolve(new Blob([this._bodyArrayBuffer]))
5105               } else if (this._bodyFormData) {
5106                 throw new Error('could not read FormData body as blob')
5107               } else {
5108                 return Promise.resolve(new Blob([this._bodyText]))
5109               }
5110             };
5111
5112             this.arrayBuffer = function() {
5113               if (this._bodyArrayBuffer) {
5114                 return consumed(this) || Promise.resolve(this._bodyArrayBuffer)
5115               } else {
5116                 return this.blob().then(readBlobAsArrayBuffer)
5117               }
5118             };
5119           }
5120
5121           this.text = function() {
5122             var rejected = consumed(this);
5123             if (rejected) {
5124               return rejected
5125             }
5126
5127             if (this._bodyBlob) {
5128               return readBlobAsText(this._bodyBlob)
5129             } else if (this._bodyArrayBuffer) {
5130               return Promise.resolve(readArrayBufferAsText(this._bodyArrayBuffer))
5131             } else if (this._bodyFormData) {
5132               throw new Error('could not read FormData body as text')
5133             } else {
5134               return Promise.resolve(this._bodyText)
5135             }
5136           };
5137
5138           if (support.formData) {
5139             this.formData = function() {
5140               return this.text().then(decode)
5141             };
5142           }
5143
5144           this.json = function() {
5145             return this.text().then(JSON.parse)
5146           };
5147
5148           return this
5149         }
5150
5151         // HTTP methods whose capitalization should be normalized
5152         var methods = ['DELETE', 'GET', 'HEAD', 'OPTIONS', 'POST', 'PUT'];
5153
5154         function normalizeMethod(method) {
5155           var upcased = method.toUpperCase();
5156           return methods.indexOf(upcased) > -1 ? upcased : method
5157         }
5158
5159         function Request(input, options) {
5160           options = options || {};
5161           var body = options.body;
5162
5163           if (input instanceof Request) {
5164             if (input.bodyUsed) {
5165               throw new TypeError('Already read')
5166             }
5167             this.url = input.url;
5168             this.credentials = input.credentials;
5169             if (!options.headers) {
5170               this.headers = new Headers(input.headers);
5171             }
5172             this.method = input.method;
5173             this.mode = input.mode;
5174             this.signal = input.signal;
5175             if (!body && input._bodyInit != null) {
5176               body = input._bodyInit;
5177               input.bodyUsed = true;
5178             }
5179           } else {
5180             this.url = String(input);
5181           }
5182
5183           this.credentials = options.credentials || this.credentials || 'same-origin';
5184           if (options.headers || !this.headers) {
5185             this.headers = new Headers(options.headers);
5186           }
5187           this.method = normalizeMethod(options.method || this.method || 'GET');
5188           this.mode = options.mode || this.mode || null;
5189           this.signal = options.signal || this.signal;
5190           this.referrer = null;
5191
5192           if ((this.method === 'GET' || this.method === 'HEAD') && body) {
5193             throw new TypeError('Body not allowed for GET or HEAD requests')
5194           }
5195           this._initBody(body);
5196
5197           if (this.method === 'GET' || this.method === 'HEAD') {
5198             if (options.cache === 'no-store' || options.cache === 'no-cache') {
5199               // Search for a '_' parameter in the query string
5200               var reParamSearch = /([?&])_=[^&]*/;
5201               if (reParamSearch.test(this.url)) {
5202                 // If it already exists then set the value with the current time
5203                 this.url = this.url.replace(reParamSearch, '$1_=' + new Date().getTime());
5204               } else {
5205                 // Otherwise add a new '_' parameter to the end with the current time
5206                 var reQueryString = /\?/;
5207                 this.url += (reQueryString.test(this.url) ? '&' : '?') + '_=' + new Date().getTime();
5208               }
5209             }
5210           }
5211         }
5212
5213         Request.prototype.clone = function() {
5214           return new Request(this, {body: this._bodyInit})
5215         };
5216
5217         function decode(body) {
5218           var form = new FormData();
5219           body
5220             .trim()
5221             .split('&')
5222             .forEach(function(bytes) {
5223               if (bytes) {
5224                 var split = bytes.split('=');
5225                 var name = split.shift().replace(/\+/g, ' ');
5226                 var value = split.join('=').replace(/\+/g, ' ');
5227                 form.append(decodeURIComponent(name), decodeURIComponent(value));
5228               }
5229             });
5230           return form
5231         }
5232
5233         function parseHeaders(rawHeaders) {
5234           var headers = new Headers();
5235           // Replace instances of \r\n and \n followed by at least one space or horizontal tab with a space
5236           // https://tools.ietf.org/html/rfc7230#section-3.2
5237           var preProcessedHeaders = rawHeaders.replace(/\r?\n[\t ]+/g, ' ');
5238           preProcessedHeaders.split(/\r?\n/).forEach(function(line) {
5239             var parts = line.split(':');
5240             var key = parts.shift().trim();
5241             if (key) {
5242               var value = parts.join(':').trim();
5243               headers.append(key, value);
5244             }
5245           });
5246           return headers
5247         }
5248
5249         Body.call(Request.prototype);
5250
5251         function Response(bodyInit, options) {
5252           if (!options) {
5253             options = {};
5254           }
5255
5256           this.type = 'default';
5257           this.status = options.status === undefined ? 200 : options.status;
5258           this.ok = this.status >= 200 && this.status < 300;
5259           this.statusText = 'statusText' in options ? options.statusText : '';
5260           this.headers = new Headers(options.headers);
5261           this.url = options.url || '';
5262           this._initBody(bodyInit);
5263         }
5264
5265         Body.call(Response.prototype);
5266
5267         Response.prototype.clone = function() {
5268           return new Response(this._bodyInit, {
5269             status: this.status,
5270             statusText: this.statusText,
5271             headers: new Headers(this.headers),
5272             url: this.url
5273           })
5274         };
5275
5276         Response.error = function() {
5277           var response = new Response(null, {status: 0, statusText: ''});
5278           response.type = 'error';
5279           return response
5280         };
5281
5282         var redirectStatuses = [301, 302, 303, 307, 308];
5283
5284         Response.redirect = function(url, status) {
5285           if (redirectStatuses.indexOf(status) === -1) {
5286             throw new RangeError('Invalid status code')
5287           }
5288
5289           return new Response(null, {status: status, headers: {location: url}})
5290         };
5291
5292         var DOMException$1 = global$1.DOMException;
5293
5294         if (typeof DOMException$1 !== 'function') {
5295           DOMException$1 = function(message, name) {
5296             this.message = message;
5297             this.name = name;
5298             var error = Error(message);
5299             this.stack = error.stack;
5300           };
5301           DOMException$1.prototype = Object.create(Error.prototype);
5302           DOMException$1.prototype.constructor = DOMException$1;
5303         }
5304
5305         function fetch$1(input, init) {
5306           return new Promise(function(resolve, reject) {
5307             var request = new Request(input, init);
5308
5309             if (request.signal && request.signal.aborted) {
5310               return reject(new DOMException$1('Aborted', 'AbortError'))
5311             }
5312
5313             var xhr = new XMLHttpRequest();
5314
5315             function abortXhr() {
5316               xhr.abort();
5317             }
5318
5319             xhr.onload = function() {
5320               var options = {
5321                 status: xhr.status,
5322                 statusText: xhr.statusText,
5323                 headers: parseHeaders(xhr.getAllResponseHeaders() || '')
5324               };
5325               options.url = 'responseURL' in xhr ? xhr.responseURL : options.headers.get('X-Request-URL');
5326               var body = 'response' in xhr ? xhr.response : xhr.responseText;
5327               setTimeout(function() {
5328                 resolve(new Response(body, options));
5329               }, 0);
5330             };
5331
5332             xhr.onerror = function() {
5333               setTimeout(function() {
5334                 reject(new TypeError('Network request failed'));
5335               }, 0);
5336             };
5337
5338             xhr.ontimeout = function() {
5339               setTimeout(function() {
5340                 reject(new TypeError('Network request failed'));
5341               }, 0);
5342             };
5343
5344             xhr.onabort = function() {
5345               setTimeout(function() {
5346                 reject(new DOMException$1('Aborted', 'AbortError'));
5347               }, 0);
5348             };
5349
5350             function fixUrl(url) {
5351               try {
5352                 return url === '' && global$1.location.href ? global$1.location.href : url
5353               } catch (e) {
5354                 return url
5355               }
5356             }
5357
5358             xhr.open(request.method, fixUrl(request.url), true);
5359
5360             if (request.credentials === 'include') {
5361               xhr.withCredentials = true;
5362             } else if (request.credentials === 'omit') {
5363               xhr.withCredentials = false;
5364             }
5365
5366             if ('responseType' in xhr) {
5367               if (support.blob) {
5368                 xhr.responseType = 'blob';
5369               } else if (
5370                 support.arrayBuffer &&
5371                 request.headers.get('Content-Type') &&
5372                 request.headers.get('Content-Type').indexOf('application/octet-stream') !== -1
5373               ) {
5374                 xhr.responseType = 'arraybuffer';
5375               }
5376             }
5377
5378             request.headers.forEach(function(value, name) {
5379               xhr.setRequestHeader(name, value);
5380             });
5381
5382             if (request.signal) {
5383               request.signal.addEventListener('abort', abortXhr);
5384
5385               xhr.onreadystatechange = function() {
5386                 // DONE (success or failure)
5387                 if (xhr.readyState === 4) {
5388                   request.signal.removeEventListener('abort', abortXhr);
5389                 }
5390               };
5391             }
5392
5393             xhr.send(typeof request._bodyInit === 'undefined' ? null : request._bodyInit);
5394           })
5395         }
5396
5397         fetch$1.polyfill = true;
5398
5399         if (!global$1.fetch) {
5400           global$1.fetch = fetch$1;
5401           global$1.Headers = Headers;
5402           global$1.Request = Request;
5403           global$1.Response = Response;
5404         }
5405
5406         var lib = createCommonjsModule(function (module, exports) {
5407         Object.defineProperty(exports, "__esModule", { value: true });
5408
5409
5410
5411
5412
5413
5414
5415
5416
5417
5418         if (!window.Set) {
5419             window.Set = es6Set;
5420         }
5421         if (!window.Map) {
5422             window.Map = es6Map;
5423         }
5424         if (!window.Promise) {
5425             window.Promise = polyfill$e;
5426             window.Promise._immediateFn = setAsap;
5427         }
5428         if (!Array.prototype.find) {
5429             array_prototype_find.shim();
5430         }
5431         if (!Array.prototype.findIndex) {
5432             array_prototype_findindex.shim();
5433         }
5434         if (!Array.from) {
5435             array_from.shim();
5436         }
5437         if (!Object.values) {
5438             object_values.shim();
5439         }
5440         if (!Object.assign) {
5441             object_assign.shim();
5442         }
5443         if (!window.requestAnimationFrame || !window.cancelAnimationFrame) {
5444             window.requestAnimationFrame = raf_1;
5445             window.cancelAnimationFrame = raf_1.cancel;
5446         }
5447
5448         var finalFetch = window.fetch;
5449         var finalPromise = window.Promise;
5450         window.fetch = function (input, init) {
5451             try {
5452                 return finalFetch(input, init);
5453             }
5454             catch (error) {
5455                 return new finalPromise(function (_, reject) { return reject(error); });
5456             }
5457         };
5458         });
5459
5460         var $Math$2 = GetIntrinsic('%Math%');
5461
5462         var $floor$1 = $Math$2.floor;
5463         var $abs$1 = $Math$2.abs;
5464
5465
5466
5467
5468         // https://www.ecma-international.org/ecma-262/6.0/#sec-isinteger
5469
5470         var IsInteger = function IsInteger(argument) {
5471                 if (typeof argument !== 'number' || _isNaN(argument) || !_isFinite(argument)) {
5472                         return false;
5473                 }
5474                 var abs = $abs$1(argument);
5475                 return $floor$1(abs) === abs;
5476         };
5477
5478         var ArrayPush = callBound('Array.prototype.push');
5479         var StringFromCharCodeSpread = callBind.apply(String.fromCharCode, null);
5480
5481         var implementation$8 = function fromCodePoint(_ /* fromCodePoint.length is 1 */) {
5482                 var arguments$1 = arguments;
5483
5484                 var MAX_SIZE = 0x4000;
5485                 var codeUnits = [];
5486                 var highSurrogate;
5487                 var lowSurrogate;
5488                 var index = -1;
5489                 var length = arguments.length;
5490                 if (!length) {
5491                         return '';
5492                 }
5493                 var result = '';
5494                 while (++index < length) {
5495                         var codePoint = ToNumber$1(arguments$1[index]);
5496                         if (
5497                                 !IsInteger(codePoint) ||
5498                                 codePoint < 0 || codePoint > 0x10FFFF // not a valid Unicode code point
5499                         ) {
5500                                 throw RangeError('Invalid code point: ' + codePoint);
5501                         }
5502                         if (codePoint <= 0xFFFF) { // BMP code point
5503                                 ArrayPush(codeUnits, codePoint);
5504                         } else { // Astral code point; split in surrogate halves
5505                                 // https://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae
5506                                 codePoint -= 0x10000;
5507                                 highSurrogate = (codePoint >> 10) + 0xD800;
5508                                 lowSurrogate = (codePoint % 0x400) + 0xDC00;
5509                                 ArrayPush(codeUnits, highSurrogate, lowSurrogate);
5510                         }
5511                         if (index + 1 == length || codeUnits.length > MAX_SIZE) {
5512                                 result += StringFromCharCodeSpread(codeUnits);
5513                                 codeUnits.length = 0;
5514                         }
5515                 }
5516                 return result;
5517         };
5518
5519         var polyfill$g = function getPolyfill() {
5520                 return String.fromCodePoint || implementation$8;
5521         };
5522
5523         var shim$d = function shimFromCodePoint() {
5524                 var polyfill = polyfill$g();
5525
5526                 if (String.fromCodePoint !== polyfill) {
5527                         defineProperties_1(String, { fromCodePoint: polyfill });
5528                 }
5529
5530                 return polyfill;
5531         };
5532
5533         /*! https://mths.be/fromcodepoint v1.0.0 by @mathias */
5534
5535         shim$d();
5536
5537         (function (factory) {
5538           
5539           factory();
5540         }((function () {
5541           function _classCallCheck(instance, Constructor) {
5542             if (!(instance instanceof Constructor)) {
5543               throw new TypeError("Cannot call a class as a function");
5544             }
5545           }
5546
5547           function _defineProperties(target, props) {
5548             for (var i = 0; i < props.length; i++) {
5549               var descriptor = props[i];
5550               descriptor.enumerable = descriptor.enumerable || false;
5551               descriptor.configurable = true;
5552               if ("value" in descriptor) { descriptor.writable = true; }
5553               Object.defineProperty(target, descriptor.key, descriptor);
5554             }
5555           }
5556
5557           function _createClass(Constructor, protoProps, staticProps) {
5558             if (protoProps) { _defineProperties(Constructor.prototype, protoProps); }
5559             if (staticProps) { _defineProperties(Constructor, staticProps); }
5560             return Constructor;
5561           }
5562
5563           function _inherits(subClass, superClass) {
5564             if (typeof superClass !== "function" && superClass !== null) {
5565               throw new TypeError("Super expression must either be null or a function");
5566             }
5567
5568             subClass.prototype = Object.create(superClass && superClass.prototype, {
5569               constructor: {
5570                 value: subClass,
5571                 writable: true,
5572                 configurable: true
5573               }
5574             });
5575             if (superClass) { _setPrototypeOf(subClass, superClass); }
5576           }
5577
5578           function _getPrototypeOf(o) {
5579             _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) {
5580               return o.__proto__ || Object.getPrototypeOf(o);
5581             };
5582             return _getPrototypeOf(o);
5583           }
5584
5585           function _setPrototypeOf(o, p) {
5586             _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) {
5587               o.__proto__ = p;
5588               return o;
5589             };
5590
5591             return _setPrototypeOf(o, p);
5592           }
5593
5594           function _assertThisInitialized(self) {
5595             if (self === void 0) {
5596               throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
5597             }
5598
5599             return self;
5600           }
5601
5602           function _possibleConstructorReturn(self, call) {
5603             if (call && (typeof call === "object" || typeof call === "function")) {
5604               return call;
5605             }
5606
5607             return _assertThisInitialized(self);
5608           }
5609
5610           function _superPropBase(object, property) {
5611             while (!Object.prototype.hasOwnProperty.call(object, property)) {
5612               object = _getPrototypeOf(object);
5613               if (object === null) { break; }
5614             }
5615
5616             return object;
5617           }
5618
5619           function _get(target, property, receiver) {
5620             if (typeof Reflect !== "undefined" && Reflect.get) {
5621               _get = Reflect.get;
5622             } else {
5623               _get = function _get(target, property, receiver) {
5624                 var base = _superPropBase(target, property);
5625
5626                 if (!base) { return; }
5627                 var desc = Object.getOwnPropertyDescriptor(base, property);
5628
5629                 if (desc.get) {
5630                   return desc.get.call(receiver);
5631                 }
5632
5633                 return desc.value;
5634               };
5635             }
5636
5637             return _get(target, property, receiver || target);
5638           }
5639
5640           var Emitter =
5641           /*#__PURE__*/
5642           function () {
5643             function Emitter() {
5644               _classCallCheck(this, Emitter);
5645
5646               Object.defineProperty(this, 'listeners', {
5647                 value: {},
5648                 writable: true,
5649                 configurable: true
5650               });
5651             }
5652
5653             _createClass(Emitter, [{
5654               key: "addEventListener",
5655               value: function addEventListener(type, callback) {
5656                 if (!(type in this.listeners)) {
5657                   this.listeners[type] = [];
5658                 }
5659
5660                 this.listeners[type].push(callback);
5661               }
5662             }, {
5663               key: "removeEventListener",
5664               value: function removeEventListener(type, callback) {
5665                 if (!(type in this.listeners)) {
5666                   return;
5667                 }
5668
5669                 var stack = this.listeners[type];
5670
5671                 for (var i = 0, l = stack.length; i < l; i++) {
5672                   if (stack[i] === callback) {
5673                     stack.splice(i, 1);
5674                     return;
5675                   }
5676                 }
5677               }
5678             }, {
5679               key: "dispatchEvent",
5680               value: function dispatchEvent(event) {
5681                 var _this = this;
5682
5683                 if (!(event.type in this.listeners)) {
5684                   return;
5685                 }
5686
5687                 var debounce = function debounce(callback) {
5688                   setTimeout(function () {
5689                     return callback.call(_this, event);
5690                   });
5691                 };
5692
5693                 var stack = this.listeners[event.type];
5694
5695                 for (var i = 0, l = stack.length; i < l; i++) {
5696                   debounce(stack[i]);
5697                 }
5698
5699                 return !event.defaultPrevented;
5700               }
5701             }]);
5702
5703             return Emitter;
5704           }();
5705
5706           var AbortSignal =
5707           /*#__PURE__*/
5708           function (_Emitter) {
5709             _inherits(AbortSignal, _Emitter);
5710
5711             function AbortSignal() {
5712               var _this2;
5713
5714               _classCallCheck(this, AbortSignal);
5715
5716               _this2 = _possibleConstructorReturn(this, _getPrototypeOf(AbortSignal).call(this)); // Some versions of babel does not transpile super() correctly for IE <= 10, if the parent
5717               // constructor has failed to run, then "this.listeners" will still be undefined and then we call
5718               // the parent constructor directly instead as a workaround. For general details, see babel bug:
5719               // https://github.com/babel/babel/issues/3041
5720               // This hack was added as a fix for the issue described here:
5721               // https://github.com/Financial-Times/polyfill-library/pull/59#issuecomment-477558042
5722
5723               if (!_this2.listeners) {
5724                 Emitter.call(_assertThisInitialized(_this2));
5725               } // Compared to assignment, Object.defineProperty makes properties non-enumerable by default and
5726               // we want Object.keys(new AbortController().signal) to be [] for compat with the native impl
5727
5728
5729               Object.defineProperty(_assertThisInitialized(_this2), 'aborted', {
5730                 value: false,
5731                 writable: true,
5732                 configurable: true
5733               });
5734               Object.defineProperty(_assertThisInitialized(_this2), 'onabort', {
5735                 value: null,
5736                 writable: true,
5737                 configurable: true
5738               });
5739               return _this2;
5740             }
5741
5742             _createClass(AbortSignal, [{
5743               key: "toString",
5744               value: function toString() {
5745                 return '[object AbortSignal]';
5746               }
5747             }, {
5748               key: "dispatchEvent",
5749               value: function dispatchEvent(event) {
5750                 if (event.type === 'abort') {
5751                   this.aborted = true;
5752
5753                   if (typeof this.onabort === 'function') {
5754                     this.onabort.call(this, event);
5755                   }
5756                 }
5757
5758                 _get(_getPrototypeOf(AbortSignal.prototype), "dispatchEvent", this).call(this, event);
5759               }
5760             }]);
5761
5762             return AbortSignal;
5763           }(Emitter);
5764           var AbortController =
5765           /*#__PURE__*/
5766           function () {
5767             function AbortController() {
5768               _classCallCheck(this, AbortController);
5769
5770               // Compared to assignment, Object.defineProperty makes properties non-enumerable by default and
5771               // we want Object.keys(new AbortController()) to be [] for compat with the native impl
5772               Object.defineProperty(this, 'signal', {
5773                 value: new AbortSignal(),
5774                 writable: true,
5775                 configurable: true
5776               });
5777             }
5778
5779             _createClass(AbortController, [{
5780               key: "abort",
5781               value: function abort() {
5782                 var event;
5783
5784                 try {
5785                   event = new Event('abort');
5786                 } catch (e) {
5787                   if (typeof document !== 'undefined') {
5788                     if (!document.createEvent) {
5789                       // For Internet Explorer 8:
5790                       event = document.createEventObject();
5791                       event.type = 'abort';
5792                     } else {
5793                       // For Internet Explorer 11:
5794                       event = document.createEvent('Event');
5795                       event.initEvent('abort', false, false);
5796                     }
5797                   } else {
5798                     // Fallback where document isn't available:
5799                     event = {
5800                       type: 'abort',
5801                       bubbles: false,
5802                       cancelable: false
5803                     };
5804                   }
5805                 }
5806
5807                 this.signal.dispatchEvent(event);
5808               }
5809             }, {
5810               key: "toString",
5811               value: function toString() {
5812                 return '[object AbortController]';
5813               }
5814             }]);
5815
5816             return AbortController;
5817           }();
5818
5819           if (typeof Symbol !== 'undefined' && Symbol.toStringTag) {
5820             // These are necessary to make sure that we get correct output for:
5821             // Object.prototype.toString.call(new AbortController())
5822             AbortController.prototype[Symbol.toStringTag] = 'AbortController';
5823             AbortSignal.prototype[Symbol.toStringTag] = 'AbortSignal';
5824           }
5825
5826           function polyfillNeeded(self) {
5827             if (self.__FORCE_INSTALL_ABORTCONTROLLER_POLYFILL) {
5828               console.log('__FORCE_INSTALL_ABORTCONTROLLER_POLYFILL=true is set, will force install polyfill');
5829               return true;
5830             } // Note that the "unfetch" minimal fetch polyfill defines fetch() without
5831             // defining window.Request, and this polyfill need to work on top of unfetch
5832             // so the below feature detection needs the !self.AbortController part.
5833             // The Request.prototype check is also needed because Safari versions 11.1.2
5834             // up to and including 12.1.x has a window.AbortController present but still
5835             // does NOT correctly implement abortable fetch:
5836             // https://bugs.webkit.org/show_bug.cgi?id=174980#c2
5837
5838
5839             return typeof self.Request === 'function' && !self.Request.prototype.hasOwnProperty('signal') || !self.AbortController;
5840           }
5841
5842           /**
5843            * Note: the "fetch.Request" default value is available for fetch imported from
5844            * the "node-fetch" package and not in browsers. This is OK since browsers
5845            * will be importing umd-polyfill.js from that path "self" is passed the
5846            * decorator so the default value will not be used (because browsers that define
5847            * fetch also has Request). One quirky setup where self.fetch exists but
5848            * self.Request does not is when the "unfetch" minimal fetch polyfill is used
5849            * on top of IE11; for this case the browser will try to use the fetch.Request
5850            * default value which in turn will be undefined but then then "if (Request)"
5851            * will ensure that you get a patched fetch but still no Request (as expected).
5852            * @param {fetch, Request = fetch.Request}
5853            * @returns {fetch: abortableFetch, Request: AbortableRequest}
5854            */
5855
5856           function abortableFetchDecorator(patchTargets) {
5857             if ('function' === typeof patchTargets) {
5858               patchTargets = {
5859                 fetch: patchTargets
5860               };
5861             }
5862
5863             var _patchTargets = patchTargets,
5864                 fetch = _patchTargets.fetch,
5865                 _patchTargets$Request = _patchTargets.Request,
5866                 NativeRequest = _patchTargets$Request === void 0 ? fetch.Request : _patchTargets$Request,
5867                 NativeAbortController = _patchTargets.AbortController,
5868                 _patchTargets$__FORCE = _patchTargets.__FORCE_INSTALL_ABORTCONTROLLER_POLYFILL,
5869                 __FORCE_INSTALL_ABORTCONTROLLER_POLYFILL = _patchTargets$__FORCE === void 0 ? false : _patchTargets$__FORCE;
5870
5871             if (!polyfillNeeded({
5872               fetch: fetch,
5873               Request: NativeRequest,
5874               AbortController: NativeAbortController,
5875               __FORCE_INSTALL_ABORTCONTROLLER_POLYFILL: __FORCE_INSTALL_ABORTCONTROLLER_POLYFILL
5876             })) {
5877               return {
5878                 fetch: fetch,
5879                 Request: Request
5880               };
5881             }
5882
5883             var Request = NativeRequest; // Note that the "unfetch" minimal fetch polyfill defines fetch() without
5884             // defining window.Request, and this polyfill need to work on top of unfetch
5885             // hence we only patch it if it's available. Also we don't patch it if signal
5886             // is already available on the Request prototype because in this case support
5887             // is present and the patching below can cause a crash since it assigns to
5888             // request.signal which is technically a read-only property. This latter error
5889             // happens when you run the main5.js node-fetch example in the repo
5890             // "abortcontroller-polyfill-examples". The exact error is:
5891             //   request.signal = init.signal;
5892             //   ^
5893             // TypeError: Cannot set property signal of #<Request> which has only a getter
5894
5895             if (Request && !Request.prototype.hasOwnProperty('signal') || __FORCE_INSTALL_ABORTCONTROLLER_POLYFILL) {
5896               Request = function Request(input, init) {
5897                 var signal;
5898
5899                 if (init && init.signal) {
5900                   signal = init.signal; // Never pass init.signal to the native Request implementation when the polyfill has
5901                   // been installed because if we're running on top of a browser with a
5902                   // working native AbortController (i.e. the polyfill was installed due to
5903                   // __FORCE_INSTALL_ABORTCONTROLLER_POLYFILL being set), then passing our
5904                   // fake AbortSignal to the native fetch will trigger:
5905                   // TypeError: Failed to construct 'Request': member signal is not of type AbortSignal.
5906
5907                   delete init.signal;
5908                 }
5909
5910                 var request = new NativeRequest(input, init);
5911
5912                 if (signal) {
5913                   Object.defineProperty(request, 'signal', {
5914                     writable: false,
5915                     enumerable: false,
5916                     configurable: true,
5917                     value: signal
5918                   });
5919                 }
5920
5921                 return request;
5922               };
5923
5924               Request.prototype = NativeRequest.prototype;
5925             }
5926
5927             var realFetch = fetch;
5928
5929             var abortableFetch = function abortableFetch(input, init) {
5930               var signal = Request && Request.prototype.isPrototypeOf(input) ? input.signal : init ? init.signal : undefined;
5931
5932               if (signal) {
5933                 var abortError;
5934
5935                 try {
5936                   abortError = new DOMException('Aborted', 'AbortError');
5937                 } catch (err) {
5938                   // IE 11 does not support calling the DOMException constructor, use a
5939                   // regular error object on it instead.
5940                   abortError = new Error('Aborted');
5941                   abortError.name = 'AbortError';
5942                 } // Return early if already aborted, thus avoiding making an HTTP request
5943
5944
5945                 if (signal.aborted) {
5946                   return Promise.reject(abortError);
5947                 } // Turn an event into a promise, reject it once `abort` is dispatched
5948
5949
5950                 var cancellation = new Promise(function (_, reject) {
5951                   signal.addEventListener('abort', function () {
5952                     return reject(abortError);
5953                   }, {
5954                     once: true
5955                   });
5956                 });
5957
5958                 if (init && init.signal) {
5959                   // Never pass .signal to the native implementation when the polyfill has
5960                   // been installed because if we're running on top of a browser with a
5961                   // working native AbortController (i.e. the polyfill was installed due to
5962                   // __FORCE_INSTALL_ABORTCONTROLLER_POLYFILL being set), then passing our
5963                   // fake AbortSignal to the native fetch will trigger:
5964                   // TypeError: Failed to execute 'fetch' on 'Window': member signal is not of type AbortSignal.
5965                   delete init.signal;
5966                 } // Return the fastest promise (don't need to wait for request to finish)
5967
5968
5969                 return Promise.race([cancellation, realFetch(input, init)]);
5970               }
5971
5972               return realFetch(input, init);
5973             };
5974
5975             return {
5976               fetch: abortableFetch,
5977               Request: Request
5978             };
5979           }
5980
5981           (function (self) {
5982
5983             if (!polyfillNeeded(self)) {
5984               return;
5985             }
5986
5987             if (!self.fetch) {
5988               console.warn('fetch() is not available, cannot install abortcontroller-polyfill');
5989               return;
5990             }
5991
5992             var _abortableFetch = abortableFetchDecorator(self),
5993                 fetch = _abortableFetch.fetch,
5994                 Request = _abortableFetch.Request;
5995
5996             self.fetch = fetch;
5997             self.Request = Request;
5998             Object.defineProperty(self, 'AbortController', {
5999               writable: true,
6000               enumerable: false,
6001               configurable: true,
6002               value: AbortController
6003             });
6004             Object.defineProperty(self, 'AbortSignal', {
6005               writable: true,
6006               enumerable: false,
6007               configurable: true,
6008               value: AbortSignal
6009             });
6010           })(typeof self !== 'undefined' ? self : commonjsGlobal);
6011
6012         })));
6013
6014         function actionAddEntity(way) {
6015             return function(graph) {
6016                 return graph.replace(way);
6017             };
6018         }
6019
6020         /*
6021         Order the nodes of a way in reverse order and reverse any direction dependent tags
6022         other than `oneway`. (We assume that correcting a backwards oneway is the primary
6023         reason for reversing a way.)
6024
6025         In addition, numeric-valued `incline` tags are negated.
6026
6027         The JOSM implementation was used as a guide, but transformations that were of unclear benefit
6028         or adjusted tags that don't seem to be used in practice were omitted.
6029
6030         References:
6031             http://wiki.openstreetmap.org/wiki/Forward_%26_backward,_left_%26_right
6032             http://wiki.openstreetmap.org/wiki/Key:direction#Steps
6033             http://wiki.openstreetmap.org/wiki/Key:incline
6034             http://wiki.openstreetmap.org/wiki/Route#Members
6035             http://josm.openstreetmap.de/browser/josm/trunk/src/org/openstreetmap/josm/corrector/ReverseWayTagCorrector.java
6036             http://wiki.openstreetmap.org/wiki/Tag:highway%3Dstop
6037             http://wiki.openstreetmap.org/wiki/Key:traffic_sign#On_a_way_or_area
6038         */
6039         function actionReverse(entityID, options) {
6040             var ignoreKey = /^.*(_|:)?(description|name|note|website|ref|source|comment|watch|attribution)(_|:)?/;
6041             var numeric = /^([+\-]?)(?=[\d.])/;
6042             var directionKey = /direction$/;
6043             var turn_lanes = /^turn:lanes:?/;
6044             var keyReplacements = [
6045                 [/:right$/, ':left'],
6046                 [/:left$/, ':right'],
6047                 [/:forward$/, ':backward'],
6048                 [/:backward$/, ':forward'],
6049                 [/:right:/, ':left:'],
6050                 [/:left:/, ':right:'],
6051                 [/:forward:/, ':backward:'],
6052                 [/:backward:/, ':forward:']
6053             ];
6054             var valueReplacements = {
6055                 left: 'right',
6056                 right: 'left',
6057                 up: 'down',
6058                 down: 'up',
6059                 forward: 'backward',
6060                 backward: 'forward',
6061                 forwards: 'backward',
6062                 backwards: 'forward',
6063             };
6064             var roleReplacements = {
6065                 forward: 'backward',
6066                 backward: 'forward',
6067                 forwards: 'backward',
6068                 backwards: 'forward'
6069             };
6070             var onewayReplacements = {
6071                 yes: '-1',
6072                 '1': '-1',
6073                 '-1': 'yes'
6074             };
6075
6076             var compassReplacements = {
6077                 N: 'S',
6078                 NNE: 'SSW',
6079                 NE: 'SW',
6080                 ENE: 'WSW',
6081                 E: 'W',
6082                 ESE: 'WNW',
6083                 SE: 'NW',
6084                 SSE: 'NNW',
6085                 S: 'N',
6086                 SSW: 'NNE',
6087                 SW: 'NE',
6088                 WSW: 'ENE',
6089                 W: 'E',
6090                 WNW: 'ESE',
6091                 NW: 'SE',
6092                 NNW: 'SSE'
6093             };
6094
6095
6096             function reverseKey(key) {
6097                 for (var i = 0; i < keyReplacements.length; ++i) {
6098                     var replacement = keyReplacements[i];
6099                     if (replacement[0].test(key)) {
6100                         return key.replace(replacement[0], replacement[1]);
6101                     }
6102                 }
6103                 return key;
6104             }
6105
6106
6107             function reverseValue(key, value, includeAbsolute) {
6108                 if (ignoreKey.test(key)) { return value; }
6109
6110                 // Turn lanes are left/right to key (not way) direction - #5674
6111                 if (turn_lanes.test(key)) {
6112                     return value;
6113
6114                 } else if (key === 'incline' && numeric.test(value)) {
6115                     return value.replace(numeric, function(_, sign) { return sign === '-' ? '' : '-'; });
6116
6117                 } else if (options && options.reverseOneway && key === 'oneway') {
6118                     return onewayReplacements[value] || value;
6119
6120                 } else if (includeAbsolute && directionKey.test(key)) {
6121                     if (compassReplacements[value]) { return compassReplacements[value]; }
6122
6123                     var degrees = parseFloat(value);
6124                     if (typeof degrees === 'number' && !isNaN(degrees)) {
6125                         if (degrees < 180) {
6126                             degrees += 180;
6127                         } else {
6128                             degrees -= 180;
6129                         }
6130                         return degrees.toString();
6131                     }
6132                 }
6133
6134                 return valueReplacements[value] || value;
6135             }
6136
6137
6138             // Reverse the direction of tags attached to the nodes - #3076
6139             function reverseNodeTags(graph, nodeIDs) {
6140                 for (var i = 0; i < nodeIDs.length; i++) {
6141                     var node = graph.hasEntity(nodeIDs[i]);
6142                     if (!node || !Object.keys(node.tags).length) { continue; }
6143
6144                     var tags = {};
6145                     for (var key in node.tags) {
6146                         tags[reverseKey(key)] = reverseValue(key, node.tags[key], node.id === entityID);
6147                     }
6148                     graph = graph.replace(node.update({tags: tags}));
6149                 }
6150                 return graph;
6151             }
6152
6153
6154             function reverseWay(graph, way) {
6155                 var nodes = way.nodes.slice().reverse();
6156                 var tags = {};
6157                 var role;
6158
6159                 for (var key in way.tags) {
6160                     tags[reverseKey(key)] = reverseValue(key, way.tags[key]);
6161                 }
6162
6163                 graph.parentRelations(way).forEach(function(relation) {
6164                     relation.members.forEach(function(member, index) {
6165                         if (member.id === way.id && (role = roleReplacements[member.role])) {
6166                             relation = relation.updateMember({role: role}, index);
6167                             graph = graph.replace(relation);
6168                         }
6169                     });
6170                 });
6171
6172                 // Reverse any associated directions on nodes on the way and then replace
6173                 // the way itself with the reversed node ids and updated way tags
6174                 return reverseNodeTags(graph, nodes)
6175                     .replace(way.update({nodes: nodes, tags: tags}));
6176             }
6177
6178
6179             var action = function(graph) {
6180                 var entity = graph.entity(entityID);
6181                 if (entity.type === 'way') {
6182                     return reverseWay(graph, entity);
6183                 }
6184                 return reverseNodeTags(graph, [entityID]);
6185             };
6186
6187             action.disabled = function(graph) {
6188                 var entity = graph.hasEntity(entityID);
6189                 if (!entity || entity.type === 'way') { return false; }
6190
6191                 for (var key in entity.tags) {
6192                     var value = entity.tags[key];
6193                     if (reverseKey(key) !== key || reverseValue(key, value, true) !== value) {
6194                         return false;
6195                     }
6196                 }
6197                 return 'nondirectional_node';
6198             };
6199
6200             action.entityID = function() {
6201                 return entityID;
6202             };
6203
6204             return action;
6205         }
6206
6207         function osmIsInterestingTag(key) {
6208             return key !== 'attribution' &&
6209                 key !== 'created_by' &&
6210                 key !== 'source' &&
6211                 key !== 'odbl' &&
6212                 key.indexOf('source:') !== 0 &&
6213                 key.indexOf('source_ref') !== 0 && // purposely exclude colon
6214                 key.indexOf('tiger:') !== 0;
6215         }
6216
6217         var osmAreaKeys = {};
6218         function osmSetAreaKeys(value) {
6219             osmAreaKeys = value;
6220         }
6221
6222         // returns an object with the tag from `tags` that implies an area geometry, if any
6223         function osmTagSuggestingArea(tags) {
6224             if (tags.area === 'yes') { return { area: 'yes' }; }
6225             if (tags.area === 'no') { return null; }
6226
6227             // `highway` and `railway` are typically linear features, but there
6228             // are a few exceptions that should be treated as areas, even in the
6229             // absence of a proper `area=yes` or `areaKeys` tag.. see #4194
6230             var lineKeys = {
6231                 highway: {
6232                     rest_area: true,
6233                     services: true
6234                 },
6235                 railway: {
6236                     roundhouse: true,
6237                     station: true,
6238                     traverser: true,
6239                     turntable: true,
6240                     wash: true
6241                 }
6242             };
6243             var returnTags = {};
6244             for (var key in tags) {
6245                 if (key in osmAreaKeys && !(tags[key] in osmAreaKeys[key])) {
6246                     returnTags[key] = tags[key];
6247                     return returnTags;
6248                 }
6249                 if (key in lineKeys && tags[key] in lineKeys[key]) {
6250                     returnTags[key] = tags[key];
6251                     return returnTags;
6252                 }
6253             }
6254             return null;
6255         }
6256
6257         // Tags that indicate a node can be a standalone point
6258         // e.g. { amenity: { bar: true, parking: true, ... } ... }
6259         var osmPointTags = {};
6260         function osmSetPointTags(value) {
6261             osmPointTags = value;
6262         }
6263         // Tags that indicate a node can be part of a way
6264         // e.g. { amenity: { parking: true, ... }, highway: { stop: true ... } ... }
6265         var osmVertexTags = {};
6266         function osmSetVertexTags(value) {
6267             osmVertexTags = value;
6268         }
6269
6270         function osmNodeGeometriesForTags(nodeTags) {
6271             var geometries = {};
6272             for (var key in nodeTags) {
6273                 if (osmPointTags[key] &&
6274                     (osmPointTags[key]['*'] || osmPointTags[key][nodeTags[key]])) {
6275                     geometries.point = true;
6276                 }
6277                 if (osmVertexTags[key] &&
6278                     (osmVertexTags[key]['*'] || osmVertexTags[key][nodeTags[key]])) {
6279                     geometries.vertex = true;
6280                 }
6281                 // break early if both are already supported
6282                 if (geometries.point && geometries.vertex) { break; }
6283             }
6284             return geometries;
6285         }
6286
6287         var osmOneWayTags = {
6288             'aerialway': {
6289                 'chair_lift': true,
6290                 'drag_lift': true,
6291                 'j-bar': true,
6292                 'magic_carpet': true,
6293                 'mixed_lift': true,
6294                 'platter': true,
6295                 'rope_tow': true,
6296                 't-bar': true,
6297                 'zip_line': true
6298             },
6299             'highway': {
6300                 'motorway': true
6301             },
6302             'junction': {
6303                 'circular': true,
6304                 'roundabout': true
6305             },
6306             'man_made': {
6307                 'goods_conveyor': true,
6308                 'piste:halfpipe': true
6309             },
6310             'piste:type': {
6311                 'downhill': true,
6312                 'sled': true,
6313                 'yes': true
6314             },
6315             'waterway': {
6316                 'canal': true,
6317                 'ditch': true,
6318                 'drain': true,
6319                 'fish_pass': true,
6320                 'river': true,
6321                 'stream': true,
6322                 'tidal_channel': true
6323             }
6324         };
6325
6326         // solid and smooth surfaces akin to the assumed default road surface in OSM
6327         var osmPavedTags = {
6328             'surface': {
6329                 'paved': true,
6330                 'asphalt': true,
6331                 'concrete': true,
6332                 'concrete:lanes': true,
6333                 'concrete:plates': true
6334             },
6335             'tracktype': {
6336                 'grade1': true
6337             }
6338         };
6339
6340         // solid, if somewhat uncommon surfaces with a high range of smoothness
6341         var osmSemipavedTags = {
6342             'surface': {
6343                 'cobblestone': true,
6344                 'cobblestone:flattened': true,
6345                 'unhewn_cobblestone': true,
6346                 'sett': true,
6347                 'paving_stones': true,
6348                 'metal': true,
6349                 'wood': true
6350             }
6351         };
6352
6353         var osmRightSideIsInsideTags = {
6354             'natural': {
6355                 'cliff': true,
6356                 'coastline': 'coastline',
6357             },
6358             'barrier': {
6359                 'retaining_wall': true,
6360                 'kerb': true,
6361                 'guard_rail': true,
6362                 'city_wall': true,
6363             },
6364             'man_made': {
6365                 'embankment': true
6366             },
6367             'waterway': {
6368                 'weir': true
6369             }
6370         };
6371
6372         // "highway" tag values for pedestrian or vehicle right-of-ways that make up the routable network
6373         // (does not include `raceway`)
6374         var osmRoutableHighwayTagValues = {
6375             motorway: true, trunk: true, primary: true, secondary: true, tertiary: true, residential: true,
6376             motorway_link: true, trunk_link: true, primary_link: true, secondary_link: true, tertiary_link: true,
6377             unclassified: true, road: true, service: true, track: true, living_street: true, bus_guideway: true,
6378             path: true, footway: true, cycleway: true, bridleway: true, pedestrian: true, corridor: true, steps: true
6379         };
6380         // "highway" tag values that generally do not allow motor vehicles
6381         var osmPathHighwayTagValues = {
6382             path: true, footway: true, cycleway: true, bridleway: true, pedestrian: true, corridor: true, steps: true
6383         };
6384
6385         // "railway" tag values representing existing railroad tracks (purposely does not include 'abandoned')
6386         var osmRailwayTrackTagValues = {
6387             rail: true, light_rail: true, tram: true, subway: true,
6388             monorail: true, funicular: true, miniature: true, narrow_gauge: true,
6389             disused: true, preserved: true
6390         };
6391
6392         // "waterway" tag values for line features representing water flow
6393         var osmFlowingWaterwayTagValues = {
6394             canal: true, ditch: true, drain: true, fish_pass: true, river: true, stream: true, tidal_channel: true
6395         };
6396
6397         // Adds floating point numbers with twice the normal precision.
6398         // Reference: J. R. Shewchuk, Adaptive Precision Floating-Point Arithmetic and
6399         // Fast Robust Geometric Predicates, Discrete & Computational Geometry 18(3)
6400         // 305–363 (1997).
6401         // Code adapted from GeographicLib by Charles F. F. Karney,
6402         // http://geographiclib.sourceforge.net/
6403
6404         function adder() {
6405           return new Adder;
6406         }
6407
6408         function Adder() {
6409           this.reset();
6410         }
6411
6412         Adder.prototype = {
6413           constructor: Adder,
6414           reset: function() {
6415             this.s = // rounded value
6416             this.t = 0; // exact error
6417           },
6418           add: function(y) {
6419             add(temp, y, this.t);
6420             add(this, temp.s, this.s);
6421             if (this.s) { this.t += temp.t; }
6422             else { this.s = temp.t; }
6423           },
6424           valueOf: function() {
6425             return this.s;
6426           }
6427         };
6428
6429         var temp = new Adder;
6430
6431         function add(adder, a, b) {
6432           var x = adder.s = a + b,
6433               bv = x - a,
6434               av = x - bv;
6435           adder.t = (a - av) + (b - bv);
6436         }
6437
6438         var epsilon = 1e-6;
6439         var epsilon2 = 1e-12;
6440         var pi = Math.PI;
6441         var halfPi = pi / 2;
6442         var quarterPi = pi / 4;
6443         var tau = pi * 2;
6444
6445         var degrees = 180 / pi;
6446         var radians = pi / 180;
6447
6448         var abs$2 = Math.abs;
6449         var atan = Math.atan;
6450         var atan2 = Math.atan2;
6451         var cos = Math.cos;
6452         var exp = Math.exp;
6453         var log = Math.log;
6454         var sin = Math.sin;
6455         var sign$2 = Math.sign || function(x) { return x > 0 ? 1 : x < 0 ? -1 : 0; };
6456         var sqrt = Math.sqrt;
6457         var tan = Math.tan;
6458
6459         function acos(x) {
6460           return x > 1 ? 0 : x < -1 ? pi : Math.acos(x);
6461         }
6462
6463         function asin(x) {
6464           return x > 1 ? halfPi : x < -1 ? -halfPi : Math.asin(x);
6465         }
6466
6467         function noop$2() {}
6468
6469         function streamGeometry(geometry, stream) {
6470           if (geometry && streamGeometryType.hasOwnProperty(geometry.type)) {
6471             streamGeometryType[geometry.type](geometry, stream);
6472           }
6473         }
6474
6475         var streamObjectType = {
6476           Feature: function(object, stream) {
6477             streamGeometry(object.geometry, stream);
6478           },
6479           FeatureCollection: function(object, stream) {
6480             var features = object.features, i = -1, n = features.length;
6481             while (++i < n) { streamGeometry(features[i].geometry, stream); }
6482           }
6483         };
6484
6485         var streamGeometryType = {
6486           Sphere: function(object, stream) {
6487             stream.sphere();
6488           },
6489           Point: function(object, stream) {
6490             object = object.coordinates;
6491             stream.point(object[0], object[1], object[2]);
6492           },
6493           MultiPoint: function(object, stream) {
6494             var coordinates = object.coordinates, i = -1, n = coordinates.length;
6495             while (++i < n) { object = coordinates[i], stream.point(object[0], object[1], object[2]); }
6496           },
6497           LineString: function(object, stream) {
6498             streamLine(object.coordinates, stream, 0);
6499           },
6500           MultiLineString: function(object, stream) {
6501             var coordinates = object.coordinates, i = -1, n = coordinates.length;
6502             while (++i < n) { streamLine(coordinates[i], stream, 0); }
6503           },
6504           Polygon: function(object, stream) {
6505             streamPolygon(object.coordinates, stream);
6506           },
6507           MultiPolygon: function(object, stream) {
6508             var coordinates = object.coordinates, i = -1, n = coordinates.length;
6509             while (++i < n) { streamPolygon(coordinates[i], stream); }
6510           },
6511           GeometryCollection: function(object, stream) {
6512             var geometries = object.geometries, i = -1, n = geometries.length;
6513             while (++i < n) { streamGeometry(geometries[i], stream); }
6514           }
6515         };
6516
6517         function streamLine(coordinates, stream, closed) {
6518           var i = -1, n = coordinates.length - closed, coordinate;
6519           stream.lineStart();
6520           while (++i < n) { coordinate = coordinates[i], stream.point(coordinate[0], coordinate[1], coordinate[2]); }
6521           stream.lineEnd();
6522         }
6523
6524         function streamPolygon(coordinates, stream) {
6525           var i = -1, n = coordinates.length;
6526           stream.polygonStart();
6527           while (++i < n) { streamLine(coordinates[i], stream, 1); }
6528           stream.polygonEnd();
6529         }
6530
6531         function d3_geoStream(object, stream) {
6532           if (object && streamObjectType.hasOwnProperty(object.type)) {
6533             streamObjectType[object.type](object, stream);
6534           } else {
6535             streamGeometry(object, stream);
6536           }
6537         }
6538
6539         var areaRingSum = adder();
6540
6541         var areaSum = adder(),
6542             lambda00,
6543             phi00,
6544             lambda0,
6545             cosPhi0,
6546             sinPhi0;
6547
6548         var areaStream = {
6549           point: noop$2,
6550           lineStart: noop$2,
6551           lineEnd: noop$2,
6552           polygonStart: function() {
6553             areaRingSum.reset();
6554             areaStream.lineStart = areaRingStart;
6555             areaStream.lineEnd = areaRingEnd;
6556           },
6557           polygonEnd: function() {
6558             var areaRing = +areaRingSum;
6559             areaSum.add(areaRing < 0 ? tau + areaRing : areaRing);
6560             this.lineStart = this.lineEnd = this.point = noop$2;
6561           },
6562           sphere: function() {
6563             areaSum.add(tau);
6564           }
6565         };
6566
6567         function areaRingStart() {
6568           areaStream.point = areaPointFirst;
6569         }
6570
6571         function areaRingEnd() {
6572           areaPoint(lambda00, phi00);
6573         }
6574
6575         function areaPointFirst(lambda, phi) {
6576           areaStream.point = areaPoint;
6577           lambda00 = lambda, phi00 = phi;
6578           lambda *= radians, phi *= radians;
6579           lambda0 = lambda, cosPhi0 = cos(phi = phi / 2 + quarterPi), sinPhi0 = sin(phi);
6580         }
6581
6582         function areaPoint(lambda, phi) {
6583           lambda *= radians, phi *= radians;
6584           phi = phi / 2 + quarterPi; // half the angular distance from south pole
6585
6586           // Spherical excess E for a spherical triangle with vertices: south pole,
6587           // previous point, current point.  Uses a formula derived from Cagnoli’s
6588           // theorem.  See Todhunter, Spherical Trig. (1871), Sec. 103, Eq. (2).
6589           var dLambda = lambda - lambda0,
6590               sdLambda = dLambda >= 0 ? 1 : -1,
6591               adLambda = sdLambda * dLambda,
6592               cosPhi = cos(phi),
6593               sinPhi = sin(phi),
6594               k = sinPhi0 * sinPhi,
6595               u = cosPhi0 * cosPhi + k * cos(adLambda),
6596               v = k * sdLambda * sin(adLambda);
6597           areaRingSum.add(atan2(v, u));
6598
6599           // Advance the previous points.
6600           lambda0 = lambda, cosPhi0 = cosPhi, sinPhi0 = sinPhi;
6601         }
6602
6603         function d3_geoArea(object) {
6604           areaSum.reset();
6605           d3_geoStream(object, areaStream);
6606           return areaSum * 2;
6607         }
6608
6609         function spherical(cartesian) {
6610           return [atan2(cartesian[1], cartesian[0]), asin(cartesian[2])];
6611         }
6612
6613         function cartesian(spherical) {
6614           var lambda = spherical[0], phi = spherical[1], cosPhi = cos(phi);
6615           return [cosPhi * cos(lambda), cosPhi * sin(lambda), sin(phi)];
6616         }
6617
6618         function cartesianDot(a, b) {
6619           return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
6620         }
6621
6622         function cartesianCross(a, b) {
6623           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]];
6624         }
6625
6626         // TODO return a
6627         function cartesianAddInPlace(a, b) {
6628           a[0] += b[0], a[1] += b[1], a[2] += b[2];
6629         }
6630
6631         function cartesianScale(vector, k) {
6632           return [vector[0] * k, vector[1] * k, vector[2] * k];
6633         }
6634
6635         // TODO return d
6636         function cartesianNormalizeInPlace(d) {
6637           var l = sqrt(d[0] * d[0] + d[1] * d[1] + d[2] * d[2]);
6638           d[0] /= l, d[1] /= l, d[2] /= l;
6639         }
6640
6641         var lambda0$1, phi0, lambda1, phi1, // bounds
6642             lambda2, // previous lambda-coordinate
6643             lambda00$1, phi00$1, // first point
6644             p0, // previous 3D point
6645             deltaSum = adder(),
6646             ranges,
6647             range;
6648
6649         var boundsStream = {
6650           point: boundsPoint,
6651           lineStart: boundsLineStart,
6652           lineEnd: boundsLineEnd,
6653           polygonStart: function() {
6654             boundsStream.point = boundsRingPoint;
6655             boundsStream.lineStart = boundsRingStart;
6656             boundsStream.lineEnd = boundsRingEnd;
6657             deltaSum.reset();
6658             areaStream.polygonStart();
6659           },
6660           polygonEnd: function() {
6661             areaStream.polygonEnd();
6662             boundsStream.point = boundsPoint;
6663             boundsStream.lineStart = boundsLineStart;
6664             boundsStream.lineEnd = boundsLineEnd;
6665             if (areaRingSum < 0) { lambda0$1 = -(lambda1 = 180), phi0 = -(phi1 = 90); }
6666             else if (deltaSum > epsilon) { phi1 = 90; }
6667             else if (deltaSum < -epsilon) { phi0 = -90; }
6668             range[0] = lambda0$1, range[1] = lambda1;
6669           },
6670           sphere: function() {
6671             lambda0$1 = -(lambda1 = 180), phi0 = -(phi1 = 90);
6672           }
6673         };
6674
6675         function boundsPoint(lambda, phi) {
6676           ranges.push(range = [lambda0$1 = lambda, lambda1 = lambda]);
6677           if (phi < phi0) { phi0 = phi; }
6678           if (phi > phi1) { phi1 = phi; }
6679         }
6680
6681         function linePoint(lambda, phi) {
6682           var p = cartesian([lambda * radians, phi * radians]);
6683           if (p0) {
6684             var normal = cartesianCross(p0, p),
6685                 equatorial = [normal[1], -normal[0], 0],
6686                 inflection = cartesianCross(equatorial, normal);
6687             cartesianNormalizeInPlace(inflection);
6688             inflection = spherical(inflection);
6689             var delta = lambda - lambda2,
6690                 sign = delta > 0 ? 1 : -1,
6691                 lambdai = inflection[0] * degrees * sign,
6692                 phii,
6693                 antimeridian = abs$2(delta) > 180;
6694             if (antimeridian ^ (sign * lambda2 < lambdai && lambdai < sign * lambda)) {
6695               phii = inflection[1] * degrees;
6696               if (phii > phi1) { phi1 = phii; }
6697             } else if (lambdai = (lambdai + 360) % 360 - 180, antimeridian ^ (sign * lambda2 < lambdai && lambdai < sign * lambda)) {
6698               phii = -inflection[1] * degrees;
6699               if (phii < phi0) { phi0 = phii; }
6700             } else {
6701               if (phi < phi0) { phi0 = phi; }
6702               if (phi > phi1) { phi1 = phi; }
6703             }
6704             if (antimeridian) {
6705               if (lambda < lambda2) {
6706                 if (angle(lambda0$1, lambda) > angle(lambda0$1, lambda1)) { lambda1 = lambda; }
6707               } else {
6708                 if (angle(lambda, lambda1) > angle(lambda0$1, lambda1)) { lambda0$1 = lambda; }
6709               }
6710             } else {
6711               if (lambda1 >= lambda0$1) {
6712                 if (lambda < lambda0$1) { lambda0$1 = lambda; }
6713                 if (lambda > lambda1) { lambda1 = lambda; }
6714               } else {
6715                 if (lambda > lambda2) {
6716                   if (angle(lambda0$1, lambda) > angle(lambda0$1, lambda1)) { lambda1 = lambda; }
6717                 } else {
6718                   if (angle(lambda, lambda1) > angle(lambda0$1, lambda1)) { lambda0$1 = lambda; }
6719                 }
6720               }
6721             }
6722           } else {
6723             ranges.push(range = [lambda0$1 = lambda, lambda1 = lambda]);
6724           }
6725           if (phi < phi0) { phi0 = phi; }
6726           if (phi > phi1) { phi1 = phi; }
6727           p0 = p, lambda2 = lambda;
6728         }
6729
6730         function boundsLineStart() {
6731           boundsStream.point = linePoint;
6732         }
6733
6734         function boundsLineEnd() {
6735           range[0] = lambda0$1, range[1] = lambda1;
6736           boundsStream.point = boundsPoint;
6737           p0 = null;
6738         }
6739
6740         function boundsRingPoint(lambda, phi) {
6741           if (p0) {
6742             var delta = lambda - lambda2;
6743             deltaSum.add(abs$2(delta) > 180 ? delta + (delta > 0 ? 360 : -360) : delta);
6744           } else {
6745             lambda00$1 = lambda, phi00$1 = phi;
6746           }
6747           areaStream.point(lambda, phi);
6748           linePoint(lambda, phi);
6749         }
6750
6751         function boundsRingStart() {
6752           areaStream.lineStart();
6753         }
6754
6755         function boundsRingEnd() {
6756           boundsRingPoint(lambda00$1, phi00$1);
6757           areaStream.lineEnd();
6758           if (abs$2(deltaSum) > epsilon) { lambda0$1 = -(lambda1 = 180); }
6759           range[0] = lambda0$1, range[1] = lambda1;
6760           p0 = null;
6761         }
6762
6763         // Finds the left-right distance between two longitudes.
6764         // This is almost the same as (lambda1 - lambda0 + 360°) % 360°, except that we want
6765         // the distance between ±180° to be 360°.
6766         function angle(lambda0, lambda1) {
6767           return (lambda1 -= lambda0) < 0 ? lambda1 + 360 : lambda1;
6768         }
6769
6770         function rangeCompare(a, b) {
6771           return a[0] - b[0];
6772         }
6773
6774         function rangeContains(range, x) {
6775           return range[0] <= range[1] ? range[0] <= x && x <= range[1] : x < range[0] || range[1] < x;
6776         }
6777
6778         function d3_geoBounds(feature) {
6779           var i, n, a, b, merged, deltaMax, delta;
6780
6781           phi1 = lambda1 = -(lambda0$1 = phi0 = Infinity);
6782           ranges = [];
6783           d3_geoStream(feature, boundsStream);
6784
6785           // First, sort ranges by their minimum longitudes.
6786           if (n = ranges.length) {
6787             ranges.sort(rangeCompare);
6788
6789             // Then, merge any ranges that overlap.
6790             for (i = 1, a = ranges[0], merged = [a]; i < n; ++i) {
6791               b = ranges[i];
6792               if (rangeContains(a, b[0]) || rangeContains(a, b[1])) {
6793                 if (angle(a[0], b[1]) > angle(a[0], a[1])) { a[1] = b[1]; }
6794                 if (angle(b[0], a[1]) > angle(a[0], a[1])) { a[0] = b[0]; }
6795               } else {
6796                 merged.push(a = b);
6797               }
6798             }
6799
6800             // Finally, find the largest gap between the merged ranges.
6801             // The final bounding box will be the inverse of this gap.
6802             for (deltaMax = -Infinity, n = merged.length - 1, i = 0, a = merged[n]; i <= n; a = b, ++i) {
6803               b = merged[i];
6804               if ((delta = angle(a[1], b[0])) > deltaMax) { deltaMax = delta, lambda0$1 = b[0], lambda1 = a[1]; }
6805             }
6806           }
6807
6808           ranges = range = null;
6809
6810           return lambda0$1 === Infinity || phi0 === Infinity
6811               ? [[NaN, NaN], [NaN, NaN]]
6812               : [[lambda0$1, phi0], [lambda1, phi1]];
6813         }
6814
6815         var W0, W1,
6816             X0, Y0, Z0,
6817             X1, Y1, Z1,
6818             X2, Y2, Z2,
6819             lambda00$2, phi00$2, // first point
6820             x0, y0, z0; // previous point
6821
6822         var centroidStream = {
6823           sphere: noop$2,
6824           point: centroidPoint,
6825           lineStart: centroidLineStart,
6826           lineEnd: centroidLineEnd,
6827           polygonStart: function() {
6828             centroidStream.lineStart = centroidRingStart;
6829             centroidStream.lineEnd = centroidRingEnd;
6830           },
6831           polygonEnd: function() {
6832             centroidStream.lineStart = centroidLineStart;
6833             centroidStream.lineEnd = centroidLineEnd;
6834           }
6835         };
6836
6837         // Arithmetic mean of Cartesian vectors.
6838         function centroidPoint(lambda, phi) {
6839           lambda *= radians, phi *= radians;
6840           var cosPhi = cos(phi);
6841           centroidPointCartesian(cosPhi * cos(lambda), cosPhi * sin(lambda), sin(phi));
6842         }
6843
6844         function centroidPointCartesian(x, y, z) {
6845           ++W0;
6846           X0 += (x - X0) / W0;
6847           Y0 += (y - Y0) / W0;
6848           Z0 += (z - Z0) / W0;
6849         }
6850
6851         function centroidLineStart() {
6852           centroidStream.point = centroidLinePointFirst;
6853         }
6854
6855         function centroidLinePointFirst(lambda, phi) {
6856           lambda *= radians, phi *= radians;
6857           var cosPhi = cos(phi);
6858           x0 = cosPhi * cos(lambda);
6859           y0 = cosPhi * sin(lambda);
6860           z0 = sin(phi);
6861           centroidStream.point = centroidLinePoint;
6862           centroidPointCartesian(x0, y0, z0);
6863         }
6864
6865         function centroidLinePoint(lambda, phi) {
6866           lambda *= radians, phi *= radians;
6867           var cosPhi = cos(phi),
6868               x = cosPhi * cos(lambda),
6869               y = cosPhi * sin(lambda),
6870               z = sin(phi),
6871               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);
6872           W1 += w;
6873           X1 += w * (x0 + (x0 = x));
6874           Y1 += w * (y0 + (y0 = y));
6875           Z1 += w * (z0 + (z0 = z));
6876           centroidPointCartesian(x0, y0, z0);
6877         }
6878
6879         function centroidLineEnd() {
6880           centroidStream.point = centroidPoint;
6881         }
6882
6883         // See J. E. Brock, The Inertia Tensor for a Spherical Triangle,
6884         // J. Applied Mechanics 42, 239 (1975).
6885         function centroidRingStart() {
6886           centroidStream.point = centroidRingPointFirst;
6887         }
6888
6889         function centroidRingEnd() {
6890           centroidRingPoint(lambda00$2, phi00$2);
6891           centroidStream.point = centroidPoint;
6892         }
6893
6894         function centroidRingPointFirst(lambda, phi) {
6895           lambda00$2 = lambda, phi00$2 = phi;
6896           lambda *= radians, phi *= radians;
6897           centroidStream.point = centroidRingPoint;
6898           var cosPhi = cos(phi);
6899           x0 = cosPhi * cos(lambda);
6900           y0 = cosPhi * sin(lambda);
6901           z0 = sin(phi);
6902           centroidPointCartesian(x0, y0, z0);
6903         }
6904
6905         function centroidRingPoint(lambda, phi) {
6906           lambda *= radians, phi *= radians;
6907           var cosPhi = cos(phi),
6908               x = cosPhi * cos(lambda),
6909               y = cosPhi * sin(lambda),
6910               z = sin(phi),
6911               cx = y0 * z - z0 * y,
6912               cy = z0 * x - x0 * z,
6913               cz = x0 * y - y0 * x,
6914               m = sqrt(cx * cx + cy * cy + cz * cz),
6915               w = asin(m), // line weight = angle
6916               v = m && -w / m; // area weight multiplier
6917           X2 += v * cx;
6918           Y2 += v * cy;
6919           Z2 += v * cz;
6920           W1 += w;
6921           X1 += w * (x0 + (x0 = x));
6922           Y1 += w * (y0 + (y0 = y));
6923           Z1 += w * (z0 + (z0 = z));
6924           centroidPointCartesian(x0, y0, z0);
6925         }
6926
6927         function d3_geoCentroid(object) {
6928           W0 = W1 =
6929           X0 = Y0 = Z0 =
6930           X1 = Y1 = Z1 =
6931           X2 = Y2 = Z2 = 0;
6932           d3_geoStream(object, centroidStream);
6933
6934           var x = X2,
6935               y = Y2,
6936               z = Z2,
6937               m = x * x + y * y + z * z;
6938
6939           // If the area-weighted ccentroid is undefined, fall back to length-weighted ccentroid.
6940           if (m < epsilon2) {
6941             x = X1, y = Y1, z = Z1;
6942             // If the feature has zero length, fall back to arithmetic mean of point vectors.
6943             if (W1 < epsilon) { x = X0, y = Y0, z = Z0; }
6944             m = x * x + y * y + z * z;
6945             // If the feature still has an undefined ccentroid, then return.
6946             if (m < epsilon2) { return [NaN, NaN]; }
6947           }
6948
6949           return [atan2(y, x) * degrees, asin(z / sqrt(m)) * degrees];
6950         }
6951
6952         function compose(a, b) {
6953
6954           function compose(x, y) {
6955             return x = a(x, y), b(x[0], x[1]);
6956           }
6957
6958           if (a.invert && b.invert) { compose.invert = function(x, y) {
6959             return x = b.invert(x, y), x && a.invert(x[0], x[1]);
6960           }; }
6961
6962           return compose;
6963         }
6964
6965         function rotationIdentity(lambda, phi) {
6966           return [abs$2(lambda) > pi ? lambda + Math.round(-lambda / tau) * tau : lambda, phi];
6967         }
6968
6969         rotationIdentity.invert = rotationIdentity;
6970
6971         function rotateRadians(deltaLambda, deltaPhi, deltaGamma) {
6972           return (deltaLambda %= tau) ? (deltaPhi || deltaGamma ? compose(rotationLambda(deltaLambda), rotationPhiGamma(deltaPhi, deltaGamma))
6973             : rotationLambda(deltaLambda))
6974             : (deltaPhi || deltaGamma ? rotationPhiGamma(deltaPhi, deltaGamma)
6975             : rotationIdentity);
6976         }
6977
6978         function forwardRotationLambda(deltaLambda) {
6979           return function(lambda, phi) {
6980             return lambda += deltaLambda, [lambda > pi ? lambda - tau : lambda < -pi ? lambda + tau : lambda, phi];
6981           };
6982         }
6983
6984         function rotationLambda(deltaLambda) {
6985           var rotation = forwardRotationLambda(deltaLambda);
6986           rotation.invert = forwardRotationLambda(-deltaLambda);
6987           return rotation;
6988         }
6989
6990         function rotationPhiGamma(deltaPhi, deltaGamma) {
6991           var cosDeltaPhi = cos(deltaPhi),
6992               sinDeltaPhi = sin(deltaPhi),
6993               cosDeltaGamma = cos(deltaGamma),
6994               sinDeltaGamma = sin(deltaGamma);
6995
6996           function rotation(lambda, phi) {
6997             var cosPhi = cos(phi),
6998                 x = cos(lambda) * cosPhi,
6999                 y = sin(lambda) * cosPhi,
7000                 z = sin(phi),
7001                 k = z * cosDeltaPhi + x * sinDeltaPhi;
7002             return [
7003               atan2(y * cosDeltaGamma - k * sinDeltaGamma, x * cosDeltaPhi - z * sinDeltaPhi),
7004               asin(k * cosDeltaGamma + y * sinDeltaGamma)
7005             ];
7006           }
7007
7008           rotation.invert = function(lambda, phi) {
7009             var cosPhi = cos(phi),
7010                 x = cos(lambda) * cosPhi,
7011                 y = sin(lambda) * cosPhi,
7012                 z = sin(phi),
7013                 k = z * cosDeltaGamma - y * sinDeltaGamma;
7014             return [
7015               atan2(y * cosDeltaGamma + z * sinDeltaGamma, x * cosDeltaPhi + k * sinDeltaPhi),
7016               asin(k * cosDeltaPhi - x * sinDeltaPhi)
7017             ];
7018           };
7019
7020           return rotation;
7021         }
7022
7023         function rotation(rotate) {
7024           rotate = rotateRadians(rotate[0] * radians, rotate[1] * radians, rotate.length > 2 ? rotate[2] * radians : 0);
7025
7026           function forward(coordinates) {
7027             coordinates = rotate(coordinates[0] * radians, coordinates[1] * radians);
7028             return coordinates[0] *= degrees, coordinates[1] *= degrees, coordinates;
7029           }
7030
7031           forward.invert = function(coordinates) {
7032             coordinates = rotate.invert(coordinates[0] * radians, coordinates[1] * radians);
7033             return coordinates[0] *= degrees, coordinates[1] *= degrees, coordinates;
7034           };
7035
7036           return forward;
7037         }
7038
7039         // Generates a circle centered at [0°, 0°], with a given radius and precision.
7040         function circleStream(stream, radius, delta, direction, t0, t1) {
7041           if (!delta) { return; }
7042           var cosRadius = cos(radius),
7043               sinRadius = sin(radius),
7044               step = direction * delta;
7045           if (t0 == null) {
7046             t0 = radius + direction * tau;
7047             t1 = radius - step / 2;
7048           } else {
7049             t0 = circleRadius(cosRadius, t0);
7050             t1 = circleRadius(cosRadius, t1);
7051             if (direction > 0 ? t0 < t1 : t0 > t1) { t0 += direction * tau; }
7052           }
7053           for (var point, t = t0; direction > 0 ? t > t1 : t < t1; t -= step) {
7054             point = spherical([cosRadius, -sinRadius * cos(t), -sinRadius * sin(t)]);
7055             stream.point(point[0], point[1]);
7056           }
7057         }
7058
7059         // Returns the signed angle of a cartesian point relative to [cosRadius, 0, 0].
7060         function circleRadius(cosRadius, point) {
7061           point = cartesian(point), point[0] -= cosRadius;
7062           cartesianNormalizeInPlace(point);
7063           var radius = acos(-point[1]);
7064           return ((-point[2] < 0 ? -radius : radius) + tau - epsilon) % tau;
7065         }
7066
7067         function clipBuffer() {
7068           var lines = [],
7069               line;
7070           return {
7071             point: function(x, y, m) {
7072               line.push([x, y, m]);
7073             },
7074             lineStart: function() {
7075               lines.push(line = []);
7076             },
7077             lineEnd: noop$2,
7078             rejoin: function() {
7079               if (lines.length > 1) { lines.push(lines.pop().concat(lines.shift())); }
7080             },
7081             result: function() {
7082               var result = lines;
7083               lines = [];
7084               line = null;
7085               return result;
7086             }
7087           };
7088         }
7089
7090         function pointEqual(a, b) {
7091           return abs$2(a[0] - b[0]) < epsilon && abs$2(a[1] - b[1]) < epsilon;
7092         }
7093
7094         function Intersection(point, points, other, entry) {
7095           this.x = point;
7096           this.z = points;
7097           this.o = other; // another intersection
7098           this.e = entry; // is an entry?
7099           this.v = false; // visited
7100           this.n = this.p = null; // next & previous
7101         }
7102
7103         // A generalized polygon clipping algorithm: given a polygon that has been cut
7104         // into its visible line segments, and rejoins the segments by interpolating
7105         // along the clip edge.
7106         function clipRejoin(segments, compareIntersection, startInside, interpolate, stream) {
7107           var subject = [],
7108               clip = [],
7109               i,
7110               n;
7111
7112           segments.forEach(function(segment) {
7113             if ((n = segment.length - 1) <= 0) { return; }
7114             var n, p0 = segment[0], p1 = segment[n], x;
7115
7116             if (pointEqual(p0, p1)) {
7117               if (!p0[2] && !p1[2]) {
7118                 stream.lineStart();
7119                 for (i = 0; i < n; ++i) { stream.point((p0 = segment[i])[0], p0[1]); }
7120                 stream.lineEnd();
7121                 return;
7122               }
7123               // handle degenerate cases by moving the point
7124               p1[0] += 2 * epsilon;
7125             }
7126
7127             subject.push(x = new Intersection(p0, segment, null, true));
7128             clip.push(x.o = new Intersection(p0, null, x, false));
7129             subject.push(x = new Intersection(p1, segment, null, false));
7130             clip.push(x.o = new Intersection(p1, null, x, true));
7131           });
7132
7133           if (!subject.length) { return; }
7134
7135           clip.sort(compareIntersection);
7136           link(subject);
7137           link(clip);
7138
7139           for (i = 0, n = clip.length; i < n; ++i) {
7140             clip[i].e = startInside = !startInside;
7141           }
7142
7143           var start = subject[0],
7144               points,
7145               point;
7146
7147           while (1) {
7148             // Find first unvisited intersection.
7149             var current = start,
7150                 isSubject = true;
7151             while (current.v) { if ((current = current.n) === start) { return; } }
7152             points = current.z;
7153             stream.lineStart();
7154             do {
7155               current.v = current.o.v = true;
7156               if (current.e) {
7157                 if (isSubject) {
7158                   for (i = 0, n = points.length; i < n; ++i) { stream.point((point = points[i])[0], point[1]); }
7159                 } else {
7160                   interpolate(current.x, current.n.x, 1, stream);
7161                 }
7162                 current = current.n;
7163               } else {
7164                 if (isSubject) {
7165                   points = current.p.z;
7166                   for (i = points.length - 1; i >= 0; --i) { stream.point((point = points[i])[0], point[1]); }
7167                 } else {
7168                   interpolate(current.x, current.p.x, -1, stream);
7169                 }
7170                 current = current.p;
7171               }
7172               current = current.o;
7173               points = current.z;
7174               isSubject = !isSubject;
7175             } while (!current.v);
7176             stream.lineEnd();
7177           }
7178         }
7179
7180         function link(array) {
7181           if (!(n = array.length)) { return; }
7182           var n,
7183               i = 0,
7184               a = array[0],
7185               b;
7186           while (++i < n) {
7187             a.n = b = array[i];
7188             b.p = a;
7189             a = b;
7190           }
7191           a.n = b = array[0];
7192           b.p = a;
7193         }
7194
7195         var sum = adder();
7196
7197         function longitude(point) {
7198           if (abs$2(point[0]) <= pi)
7199             { return point[0]; }
7200           else
7201             { return sign$2(point[0]) * ((abs$2(point[0]) + pi) % tau - pi); }
7202         }
7203
7204         function polygonContains(polygon, point) {
7205           var lambda = longitude(point),
7206               phi = point[1],
7207               sinPhi = sin(phi),
7208               normal = [sin(lambda), -cos(lambda), 0],
7209               angle = 0,
7210               winding = 0;
7211
7212           sum.reset();
7213
7214           if (sinPhi === 1) { phi = halfPi + epsilon; }
7215           else if (sinPhi === -1) { phi = -halfPi - epsilon; }
7216
7217           for (var i = 0, n = polygon.length; i < n; ++i) {
7218             if (!(m = (ring = polygon[i]).length)) { continue; }
7219             var ring,
7220                 m,
7221                 point0 = ring[m - 1],
7222                 lambda0 = longitude(point0),
7223                 phi0 = point0[1] / 2 + quarterPi,
7224                 sinPhi0 = sin(phi0),
7225                 cosPhi0 = cos(phi0);
7226
7227             for (var j = 0; j < m; ++j, lambda0 = lambda1, sinPhi0 = sinPhi1, cosPhi0 = cosPhi1, point0 = point1) {
7228               var point1 = ring[j],
7229                   lambda1 = longitude(point1),
7230                   phi1 = point1[1] / 2 + quarterPi,
7231                   sinPhi1 = sin(phi1),
7232                   cosPhi1 = cos(phi1),
7233                   delta = lambda1 - lambda0,
7234                   sign = delta >= 0 ? 1 : -1,
7235                   absDelta = sign * delta,
7236                   antimeridian = absDelta > pi,
7237                   k = sinPhi0 * sinPhi1;
7238
7239               sum.add(atan2(k * sign * sin(absDelta), cosPhi0 * cosPhi1 + k * cos(absDelta)));
7240               angle += antimeridian ? delta + sign * tau : delta;
7241
7242               // Are the longitudes either side of the point’s meridian (lambda),
7243               // and are the latitudes smaller than the parallel (phi)?
7244               if (antimeridian ^ lambda0 >= lambda ^ lambda1 >= lambda) {
7245                 var arc = cartesianCross(cartesian(point0), cartesian(point1));
7246                 cartesianNormalizeInPlace(arc);
7247                 var intersection = cartesianCross(normal, arc);
7248                 cartesianNormalizeInPlace(intersection);
7249                 var phiArc = (antimeridian ^ delta >= 0 ? -1 : 1) * asin(intersection[2]);
7250                 if (phi > phiArc || phi === phiArc && (arc[0] || arc[1])) {
7251                   winding += antimeridian ^ delta >= 0 ? 1 : -1;
7252                 }
7253               }
7254             }
7255           }
7256
7257           // First, determine whether the South pole is inside or outside:
7258           //
7259           // It is inside if:
7260           // * the polygon winds around it in a clockwise direction.
7261           // * the polygon does not (cumulatively) wind around it, but has a negative
7262           //   (counter-clockwise) area.
7263           //
7264           // Second, count the (signed) number of times a segment crosses a lambda
7265           // from the point to the South pole.  If it is zero, then the point is the
7266           // same side as the South pole.
7267
7268           return (angle < -epsilon || angle < epsilon && sum < -epsilon) ^ (winding & 1);
7269         }
7270
7271         function d3_ascending(a, b) {
7272           return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN;
7273         }
7274
7275         function d3_bisector(compare) {
7276           if (compare.length === 1) { compare = ascendingComparator(compare); }
7277           return {
7278             left: function(a, x, lo, hi) {
7279               if (lo == null) { lo = 0; }
7280               if (hi == null) { hi = a.length; }
7281               while (lo < hi) {
7282                 var mid = lo + hi >>> 1;
7283                 if (compare(a[mid], x) < 0) { lo = mid + 1; }
7284                 else { hi = mid; }
7285               }
7286               return lo;
7287             },
7288             right: function(a, x, lo, hi) {
7289               if (lo == null) { lo = 0; }
7290               if (hi == null) { hi = a.length; }
7291               while (lo < hi) {
7292                 var mid = lo + hi >>> 1;
7293                 if (compare(a[mid], x) > 0) { hi = mid; }
7294                 else { lo = mid + 1; }
7295               }
7296               return lo;
7297             }
7298           };
7299         }
7300
7301         function ascendingComparator(f) {
7302           return function(d, x) {
7303             return d3_ascending(f(d), x);
7304           };
7305         }
7306
7307         var ascendingBisect = d3_bisector(d3_ascending);
7308         var bisectRight = ascendingBisect.right;
7309
7310         function d3_descending(a, b) {
7311           return b < a ? -1 : b > a ? 1 : b >= a ? 0 : NaN;
7312         }
7313
7314         function number(x) {
7315           return x === null ? NaN : +x;
7316         }
7317
7318         function range$1(start, stop, step) {
7319           start = +start, stop = +stop, step = (n = arguments.length) < 2 ? (stop = start, start = 0, 1) : n < 3 ? 1 : +step;
7320
7321           var i = -1,
7322               n = Math.max(0, Math.ceil((stop - start) / step)) | 0,
7323               range = new Array(n);
7324
7325           while (++i < n) {
7326             range[i] = start + i * step;
7327           }
7328
7329           return range;
7330         }
7331
7332         var e10 = Math.sqrt(50),
7333             e5 = Math.sqrt(10),
7334             e2 = Math.sqrt(2);
7335
7336         function ticks(start, stop, count) {
7337           var reverse,
7338               i = -1,
7339               n,
7340               ticks,
7341               step;
7342
7343           stop = +stop, start = +start, count = +count;
7344           if (start === stop && count > 0) { return [start]; }
7345           if (reverse = stop < start) { n = start, start = stop, stop = n; }
7346           if ((step = tickIncrement(start, stop, count)) === 0 || !isFinite(step)) { return []; }
7347
7348           if (step > 0) {
7349             start = Math.ceil(start / step);
7350             stop = Math.floor(stop / step);
7351             ticks = new Array(n = Math.ceil(stop - start + 1));
7352             while (++i < n) { ticks[i] = (start + i) * step; }
7353           } else {
7354             start = Math.floor(start * step);
7355             stop = Math.ceil(stop * step);
7356             ticks = new Array(n = Math.ceil(start - stop + 1));
7357             while (++i < n) { ticks[i] = (start - i) / step; }
7358           }
7359
7360           if (reverse) { ticks.reverse(); }
7361
7362           return ticks;
7363         }
7364
7365         function tickIncrement(start, stop, count) {
7366           var step = (stop - start) / Math.max(0, count),
7367               power = Math.floor(Math.log(step) / Math.LN10),
7368               error = step / Math.pow(10, power);
7369           return power >= 0
7370               ? (error >= e10 ? 10 : error >= e5 ? 5 : error >= e2 ? 2 : 1) * Math.pow(10, power)
7371               : -Math.pow(10, -power) / (error >= e10 ? 10 : error >= e5 ? 5 : error >= e2 ? 2 : 1);
7372         }
7373
7374         function tickStep(start, stop, count) {
7375           var step0 = Math.abs(stop - start) / Math.max(0, count),
7376               step1 = Math.pow(10, Math.floor(Math.log(step0) / Math.LN10)),
7377               error = step0 / step1;
7378           if (error >= e10) { step1 *= 10; }
7379           else if (error >= e5) { step1 *= 5; }
7380           else if (error >= e2) { step1 *= 2; }
7381           return stop < start ? -step1 : step1;
7382         }
7383
7384         function threshold(values, p, valueof) {
7385           if (valueof == null) { valueof = number; }
7386           if (!(n = values.length)) { return; }
7387           if ((p = +p) <= 0 || n < 2) { return +valueof(values[0], 0, values); }
7388           if (p >= 1) { return +valueof(values[n - 1], n - 1, values); }
7389           var n,
7390               i = (n - 1) * p,
7391               i0 = Math.floor(i),
7392               value0 = +valueof(values[i0], i0, values),
7393               value1 = +valueof(values[i0 + 1], i0 + 1, values);
7394           return value0 + (value1 - value0) * (i - i0);
7395         }
7396
7397         function d3_median(values, valueof) {
7398           var n = values.length,
7399               i = -1,
7400               value,
7401               numbers = [];
7402
7403           if (valueof == null) {
7404             while (++i < n) {
7405               if (!isNaN(value = number(values[i]))) {
7406                 numbers.push(value);
7407               }
7408             }
7409           }
7410
7411           else {
7412             while (++i < n) {
7413               if (!isNaN(value = number(valueof(values[i], i, values)))) {
7414                 numbers.push(value);
7415               }
7416             }
7417           }
7418
7419           return threshold(numbers.sort(d3_ascending), 0.5);
7420         }
7421
7422         function merge(arrays) {
7423           var n = arrays.length,
7424               m,
7425               i = -1,
7426               j = 0,
7427               merged,
7428               array;
7429
7430           while (++i < n) { j += arrays[i].length; }
7431           merged = new Array(j);
7432
7433           while (--n >= 0) {
7434             array = arrays[n];
7435             m = array.length;
7436             while (--m >= 0) {
7437               merged[--j] = array[m];
7438             }
7439           }
7440
7441           return merged;
7442         }
7443
7444         function clip(pointVisible, clipLine, interpolate, start) {
7445           return function(sink) {
7446             var line = clipLine(sink),
7447                 ringBuffer = clipBuffer(),
7448                 ringSink = clipLine(ringBuffer),
7449                 polygonStarted = false,
7450                 polygon,
7451                 segments,
7452                 ring;
7453
7454             var clip = {
7455               point: point,
7456               lineStart: lineStart,
7457               lineEnd: lineEnd,
7458               polygonStart: function() {
7459                 clip.point = pointRing;
7460                 clip.lineStart = ringStart;
7461                 clip.lineEnd = ringEnd;
7462                 segments = [];
7463                 polygon = [];
7464               },
7465               polygonEnd: function() {
7466                 clip.point = point;
7467                 clip.lineStart = lineStart;
7468                 clip.lineEnd = lineEnd;
7469                 segments = merge(segments);
7470                 var startInside = polygonContains(polygon, start);
7471                 if (segments.length) {
7472                   if (!polygonStarted) { sink.polygonStart(), polygonStarted = true; }
7473                   clipRejoin(segments, compareIntersection, startInside, interpolate, sink);
7474                 } else if (startInside) {
7475                   if (!polygonStarted) { sink.polygonStart(), polygonStarted = true; }
7476                   sink.lineStart();
7477                   interpolate(null, null, 1, sink);
7478                   sink.lineEnd();
7479                 }
7480                 if (polygonStarted) { sink.polygonEnd(), polygonStarted = false; }
7481                 segments = polygon = null;
7482               },
7483               sphere: function() {
7484                 sink.polygonStart();
7485                 sink.lineStart();
7486                 interpolate(null, null, 1, sink);
7487                 sink.lineEnd();
7488                 sink.polygonEnd();
7489               }
7490             };
7491
7492             function point(lambda, phi) {
7493               if (pointVisible(lambda, phi)) { sink.point(lambda, phi); }
7494             }
7495
7496             function pointLine(lambda, phi) {
7497               line.point(lambda, phi);
7498             }
7499
7500             function lineStart() {
7501               clip.point = pointLine;
7502               line.lineStart();
7503             }
7504
7505             function lineEnd() {
7506               clip.point = point;
7507               line.lineEnd();
7508             }
7509
7510             function pointRing(lambda, phi) {
7511               ring.push([lambda, phi]);
7512               ringSink.point(lambda, phi);
7513             }
7514
7515             function ringStart() {
7516               ringSink.lineStart();
7517               ring = [];
7518             }
7519
7520             function ringEnd() {
7521               pointRing(ring[0][0], ring[0][1]);
7522               ringSink.lineEnd();
7523
7524               var clean = ringSink.clean(),
7525                   ringSegments = ringBuffer.result(),
7526                   i, n = ringSegments.length, m,
7527                   segment,
7528                   point;
7529
7530               ring.pop();
7531               polygon.push(ring);
7532               ring = null;
7533
7534               if (!n) { return; }
7535
7536               // No intersections.
7537               if (clean & 1) {
7538                 segment = ringSegments[0];
7539                 if ((m = segment.length - 1) > 0) {
7540                   if (!polygonStarted) { sink.polygonStart(), polygonStarted = true; }
7541                   sink.lineStart();
7542                   for (i = 0; i < m; ++i) { sink.point((point = segment[i])[0], point[1]); }
7543                   sink.lineEnd();
7544                 }
7545                 return;
7546               }
7547
7548               // Rejoin connected segments.
7549               // TODO reuse ringBuffer.rejoin()?
7550               if (n > 1 && clean & 2) { ringSegments.push(ringSegments.pop().concat(ringSegments.shift())); }
7551
7552               segments.push(ringSegments.filter(validSegment));
7553             }
7554
7555             return clip;
7556           };
7557         }
7558
7559         function validSegment(segment) {
7560           return segment.length > 1;
7561         }
7562
7563         // Intersections are sorted along the clip edge. For both antimeridian cutting
7564         // and circle clipping, the same comparison is used.
7565         function compareIntersection(a, b) {
7566           return ((a = a.x)[0] < 0 ? a[1] - halfPi - epsilon : halfPi - a[1])
7567                - ((b = b.x)[0] < 0 ? b[1] - halfPi - epsilon : halfPi - b[1]);
7568         }
7569
7570         var clipAntimeridian = clip(
7571           function() { return true; },
7572           clipAntimeridianLine,
7573           clipAntimeridianInterpolate,
7574           [-pi, -halfPi]
7575         );
7576
7577         // Takes a line and cuts into visible segments. Return values: 0 - there were
7578         // intersections or the line was empty; 1 - no intersections; 2 - there were
7579         // intersections, and the first and last segments should be rejoined.
7580         function clipAntimeridianLine(stream) {
7581           var lambda0 = NaN,
7582               phi0 = NaN,
7583               sign0 = NaN,
7584               clean; // no intersections
7585
7586           return {
7587             lineStart: function() {
7588               stream.lineStart();
7589               clean = 1;
7590             },
7591             point: function(lambda1, phi1) {
7592               var sign1 = lambda1 > 0 ? pi : -pi,
7593                   delta = abs$2(lambda1 - lambda0);
7594               if (abs$2(delta - pi) < epsilon) { // line crosses a pole
7595                 stream.point(lambda0, phi0 = (phi0 + phi1) / 2 > 0 ? halfPi : -halfPi);
7596                 stream.point(sign0, phi0);
7597                 stream.lineEnd();
7598                 stream.lineStart();
7599                 stream.point(sign1, phi0);
7600                 stream.point(lambda1, phi0);
7601                 clean = 0;
7602               } else if (sign0 !== sign1 && delta >= pi) { // line crosses antimeridian
7603                 if (abs$2(lambda0 - sign0) < epsilon) { lambda0 -= sign0 * epsilon; } // handle degeneracies
7604                 if (abs$2(lambda1 - sign1) < epsilon) { lambda1 -= sign1 * epsilon; }
7605                 phi0 = clipAntimeridianIntersect(lambda0, phi0, lambda1, phi1);
7606                 stream.point(sign0, phi0);
7607                 stream.lineEnd();
7608                 stream.lineStart();
7609                 stream.point(sign1, phi0);
7610                 clean = 0;
7611               }
7612               stream.point(lambda0 = lambda1, phi0 = phi1);
7613               sign0 = sign1;
7614             },
7615             lineEnd: function() {
7616               stream.lineEnd();
7617               lambda0 = phi0 = NaN;
7618             },
7619             clean: function() {
7620               return 2 - clean; // if intersections, rejoin first and last segments
7621             }
7622           };
7623         }
7624
7625         function clipAntimeridianIntersect(lambda0, phi0, lambda1, phi1) {
7626           var cosPhi0,
7627               cosPhi1,
7628               sinLambda0Lambda1 = sin(lambda0 - lambda1);
7629           return abs$2(sinLambda0Lambda1) > epsilon
7630               ? atan((sin(phi0) * (cosPhi1 = cos(phi1)) * sin(lambda1)
7631                   - sin(phi1) * (cosPhi0 = cos(phi0)) * sin(lambda0))
7632                   / (cosPhi0 * cosPhi1 * sinLambda0Lambda1))
7633               : (phi0 + phi1) / 2;
7634         }
7635
7636         function clipAntimeridianInterpolate(from, to, direction, stream) {
7637           var phi;
7638           if (from == null) {
7639             phi = direction * halfPi;
7640             stream.point(-pi, phi);
7641             stream.point(0, phi);
7642             stream.point(pi, phi);
7643             stream.point(pi, 0);
7644             stream.point(pi, -phi);
7645             stream.point(0, -phi);
7646             stream.point(-pi, -phi);
7647             stream.point(-pi, 0);
7648             stream.point(-pi, phi);
7649           } else if (abs$2(from[0] - to[0]) > epsilon) {
7650             var lambda = from[0] < to[0] ? pi : -pi;
7651             phi = direction * lambda / 2;
7652             stream.point(-lambda, phi);
7653             stream.point(0, phi);
7654             stream.point(lambda, phi);
7655           } else {
7656             stream.point(to[0], to[1]);
7657           }
7658         }
7659
7660         function clipCircle(radius) {
7661           var cr = cos(radius),
7662               delta = 6 * radians,
7663               smallRadius = cr > 0,
7664               notHemisphere = abs$2(cr) > epsilon; // TODO optimise for this common case
7665
7666           function interpolate(from, to, direction, stream) {
7667             circleStream(stream, radius, delta, direction, from, to);
7668           }
7669
7670           function visible(lambda, phi) {
7671             return cos(lambda) * cos(phi) > cr;
7672           }
7673
7674           // Takes a line and cuts into visible segments. Return values used for polygon
7675           // clipping: 0 - there were intersections or the line was empty; 1 - no
7676           // intersections 2 - there were intersections, and the first and last segments
7677           // should be rejoined.
7678           function clipLine(stream) {
7679             var point0, // previous point
7680                 c0, // code for previous point
7681                 v0, // visibility of previous point
7682                 v00, // visibility of first point
7683                 clean; // no intersections
7684             return {
7685               lineStart: function() {
7686                 v00 = v0 = false;
7687                 clean = 1;
7688               },
7689               point: function(lambda, phi) {
7690                 var point1 = [lambda, phi],
7691                     point2,
7692                     v = visible(lambda, phi),
7693                     c = smallRadius
7694                       ? v ? 0 : code(lambda, phi)
7695                       : v ? code(lambda + (lambda < 0 ? pi : -pi), phi) : 0;
7696                 if (!point0 && (v00 = v0 = v)) { stream.lineStart(); }
7697                 if (v !== v0) {
7698                   point2 = intersect(point0, point1);
7699                   if (!point2 || pointEqual(point0, point2) || pointEqual(point1, point2))
7700                     { point1[2] = 1; }
7701                 }
7702                 if (v !== v0) {
7703                   clean = 0;
7704                   if (v) {
7705                     // outside going in
7706                     stream.lineStart();
7707                     point2 = intersect(point1, point0);
7708                     stream.point(point2[0], point2[1]);
7709                   } else {
7710                     // inside going out
7711                     point2 = intersect(point0, point1);
7712                     stream.point(point2[0], point2[1], 2);
7713                     stream.lineEnd();
7714                   }
7715                   point0 = point2;
7716                 } else if (notHemisphere && point0 && smallRadius ^ v) {
7717                   var t;
7718                   // If the codes for two points are different, or are both zero,
7719                   // and there this segment intersects with the small circle.
7720                   if (!(c & c0) && (t = intersect(point1, point0, true))) {
7721                     clean = 0;
7722                     if (smallRadius) {
7723                       stream.lineStart();
7724                       stream.point(t[0][0], t[0][1]);
7725                       stream.point(t[1][0], t[1][1]);
7726                       stream.lineEnd();
7727                     } else {
7728                       stream.point(t[1][0], t[1][1]);
7729                       stream.lineEnd();
7730                       stream.lineStart();
7731                       stream.point(t[0][0], t[0][1], 3);
7732                     }
7733                   }
7734                 }
7735                 if (v && (!point0 || !pointEqual(point0, point1))) {
7736                   stream.point(point1[0], point1[1]);
7737                 }
7738                 point0 = point1, v0 = v, c0 = c;
7739               },
7740               lineEnd: function() {
7741                 if (v0) { stream.lineEnd(); }
7742                 point0 = null;
7743               },
7744               // Rejoin first and last segments if there were intersections and the first
7745               // and last points were visible.
7746               clean: function() {
7747                 return clean | ((v00 && v0) << 1);
7748               }
7749             };
7750           }
7751
7752           // Intersects the great circle between a and b with the clip circle.
7753           function intersect(a, b, two) {
7754             var pa = cartesian(a),
7755                 pb = cartesian(b);
7756
7757             // We have two planes, n1.p = d1 and n2.p = d2.
7758             // Find intersection line p(t) = c1 n1 + c2 n2 + t (n1 ⨯ n2).
7759             var n1 = [1, 0, 0], // normal
7760                 n2 = cartesianCross(pa, pb),
7761                 n2n2 = cartesianDot(n2, n2),
7762                 n1n2 = n2[0], // cartesianDot(n1, n2),
7763                 determinant = n2n2 - n1n2 * n1n2;
7764
7765             // Two polar points.
7766             if (!determinant) { return !two && a; }
7767
7768             var c1 =  cr * n2n2 / determinant,
7769                 c2 = -cr * n1n2 / determinant,
7770                 n1xn2 = cartesianCross(n1, n2),
7771                 A = cartesianScale(n1, c1),
7772                 B = cartesianScale(n2, c2);
7773             cartesianAddInPlace(A, B);
7774
7775             // Solve |p(t)|^2 = 1.
7776             var u = n1xn2,
7777                 w = cartesianDot(A, u),
7778                 uu = cartesianDot(u, u),
7779                 t2 = w * w - uu * (cartesianDot(A, A) - 1);
7780
7781             if (t2 < 0) { return; }
7782
7783             var t = sqrt(t2),
7784                 q = cartesianScale(u, (-w - t) / uu);
7785             cartesianAddInPlace(q, A);
7786             q = spherical(q);
7787
7788             if (!two) { return q; }
7789
7790             // Two intersection points.
7791             var lambda0 = a[0],
7792                 lambda1 = b[0],
7793                 phi0 = a[1],
7794                 phi1 = b[1],
7795                 z;
7796
7797             if (lambda1 < lambda0) { z = lambda0, lambda0 = lambda1, lambda1 = z; }
7798
7799             var delta = lambda1 - lambda0,
7800                 polar = abs$2(delta - pi) < epsilon,
7801                 meridian = polar || delta < epsilon;
7802
7803             if (!polar && phi1 < phi0) { z = phi0, phi0 = phi1, phi1 = z; }
7804
7805             // Check that the first point is between a and b.
7806             if (meridian
7807                 ? polar
7808                   ? phi0 + phi1 > 0 ^ q[1] < (abs$2(q[0] - lambda0) < epsilon ? phi0 : phi1)
7809                   : phi0 <= q[1] && q[1] <= phi1
7810                 : delta > pi ^ (lambda0 <= q[0] && q[0] <= lambda1)) {
7811               var q1 = cartesianScale(u, (-w + t) / uu);
7812               cartesianAddInPlace(q1, A);
7813               return [q, spherical(q1)];
7814             }
7815           }
7816
7817           // Generates a 4-bit vector representing the location of a point relative to
7818           // the small circle's bounding box.
7819           function code(lambda, phi) {
7820             var r = smallRadius ? radius : pi - radius,
7821                 code = 0;
7822             if (lambda < -r) { code |= 1; } // left
7823             else if (lambda > r) { code |= 2; } // right
7824             if (phi < -r) { code |= 4; } // below
7825             else if (phi > r) { code |= 8; } // above
7826             return code;
7827           }
7828
7829           return clip(visible, clipLine, interpolate, smallRadius ? [0, -radius] : [-pi, radius - pi]);
7830         }
7831
7832         function clipLine(a, b, x0, y0, x1, y1) {
7833           var ax = a[0],
7834               ay = a[1],
7835               bx = b[0],
7836               by = b[1],
7837               t0 = 0,
7838               t1 = 1,
7839               dx = bx - ax,
7840               dy = by - ay,
7841               r;
7842
7843           r = x0 - ax;
7844           if (!dx && r > 0) { return; }
7845           r /= dx;
7846           if (dx < 0) {
7847             if (r < t0) { return; }
7848             if (r < t1) { t1 = r; }
7849           } else if (dx > 0) {
7850             if (r > t1) { return; }
7851             if (r > t0) { t0 = r; }
7852           }
7853
7854           r = x1 - ax;
7855           if (!dx && r < 0) { return; }
7856           r /= dx;
7857           if (dx < 0) {
7858             if (r > t1) { return; }
7859             if (r > t0) { t0 = r; }
7860           } else if (dx > 0) {
7861             if (r < t0) { return; }
7862             if (r < t1) { t1 = r; }
7863           }
7864
7865           r = y0 - ay;
7866           if (!dy && r > 0) { return; }
7867           r /= dy;
7868           if (dy < 0) {
7869             if (r < t0) { return; }
7870             if (r < t1) { t1 = r; }
7871           } else if (dy > 0) {
7872             if (r > t1) { return; }
7873             if (r > t0) { t0 = r; }
7874           }
7875
7876           r = y1 - ay;
7877           if (!dy && r < 0) { return; }
7878           r /= dy;
7879           if (dy < 0) {
7880             if (r > t1) { return; }
7881             if (r > t0) { t0 = r; }
7882           } else if (dy > 0) {
7883             if (r < t0) { return; }
7884             if (r < t1) { t1 = r; }
7885           }
7886
7887           if (t0 > 0) { a[0] = ax + t0 * dx, a[1] = ay + t0 * dy; }
7888           if (t1 < 1) { b[0] = ax + t1 * dx, b[1] = ay + t1 * dy; }
7889           return true;
7890         }
7891
7892         var clipMax = 1e9, clipMin = -clipMax;
7893
7894         // TODO Use d3-polygon’s polygonContains here for the ring check?
7895         // TODO Eliminate duplicate buffering in clipBuffer and polygon.push?
7896
7897         function clipRectangle(x0, y0, x1, y1) {
7898
7899           function visible(x, y) {
7900             return x0 <= x && x <= x1 && y0 <= y && y <= y1;
7901           }
7902
7903           function interpolate(from, to, direction, stream) {
7904             var a = 0, a1 = 0;
7905             if (from == null
7906                 || (a = corner(from, direction)) !== (a1 = corner(to, direction))
7907                 || comparePoint(from, to) < 0 ^ direction > 0) {
7908               do { stream.point(a === 0 || a === 3 ? x0 : x1, a > 1 ? y1 : y0); }
7909               while ((a = (a + direction + 4) % 4) !== a1);
7910             } else {
7911               stream.point(to[0], to[1]);
7912             }
7913           }
7914
7915           function corner(p, direction) {
7916             return abs$2(p[0] - x0) < epsilon ? direction > 0 ? 0 : 3
7917                 : abs$2(p[0] - x1) < epsilon ? direction > 0 ? 2 : 1
7918                 : abs$2(p[1] - y0) < epsilon ? direction > 0 ? 1 : 0
7919                 : direction > 0 ? 3 : 2; // abs(p[1] - y1) < epsilon
7920           }
7921
7922           function compareIntersection(a, b) {
7923             return comparePoint(a.x, b.x);
7924           }
7925
7926           function comparePoint(a, b) {
7927             var ca = corner(a, 1),
7928                 cb = corner(b, 1);
7929             return ca !== cb ? ca - cb
7930                 : ca === 0 ? b[1] - a[1]
7931                 : ca === 1 ? a[0] - b[0]
7932                 : ca === 2 ? a[1] - b[1]
7933                 : b[0] - a[0];
7934           }
7935
7936           return function(stream) {
7937             var activeStream = stream,
7938                 bufferStream = clipBuffer(),
7939                 segments,
7940                 polygon,
7941                 ring,
7942                 x__, y__, v__, // first point
7943                 x_, y_, v_, // previous point
7944                 first,
7945                 clean;
7946
7947             var clipStream = {
7948               point: point,
7949               lineStart: lineStart,
7950               lineEnd: lineEnd,
7951               polygonStart: polygonStart,
7952               polygonEnd: polygonEnd
7953             };
7954
7955             function point(x, y) {
7956               if (visible(x, y)) { activeStream.point(x, y); }
7957             }
7958
7959             function polygonInside() {
7960               var winding = 0;
7961
7962               for (var i = 0, n = polygon.length; i < n; ++i) {
7963                 for (var ring = polygon[i], j = 1, m = ring.length, point = ring[0], a0, a1, b0 = point[0], b1 = point[1]; j < m; ++j) {
7964                   a0 = b0, a1 = b1, point = ring[j], b0 = point[0], b1 = point[1];
7965                   if (a1 <= y1) { if (b1 > y1 && (b0 - a0) * (y1 - a1) > (b1 - a1) * (x0 - a0)) { ++winding; } }
7966                   else { if (b1 <= y1 && (b0 - a0) * (y1 - a1) < (b1 - a1) * (x0 - a0)) { --winding; } }
7967                 }
7968               }
7969
7970               return winding;
7971             }
7972
7973             // Buffer geometry within a polygon and then clip it en masse.
7974             function polygonStart() {
7975               activeStream = bufferStream, segments = [], polygon = [], clean = true;
7976             }
7977
7978             function polygonEnd() {
7979               var startInside = polygonInside(),
7980                   cleanInside = clean && startInside,
7981                   visible = (segments = merge(segments)).length;
7982               if (cleanInside || visible) {
7983                 stream.polygonStart();
7984                 if (cleanInside) {
7985                   stream.lineStart();
7986                   interpolate(null, null, 1, stream);
7987                   stream.lineEnd();
7988                 }
7989                 if (visible) {
7990                   clipRejoin(segments, compareIntersection, startInside, interpolate, stream);
7991                 }
7992                 stream.polygonEnd();
7993               }
7994               activeStream = stream, segments = polygon = ring = null;
7995             }
7996
7997             function lineStart() {
7998               clipStream.point = linePoint;
7999               if (polygon) { polygon.push(ring = []); }
8000               first = true;
8001               v_ = false;
8002               x_ = y_ = NaN;
8003             }
8004
8005             // TODO rather than special-case polygons, simply handle them separately.
8006             // Ideally, coincident intersection points should be jittered to avoid
8007             // clipping issues.
8008             function lineEnd() {
8009               if (segments) {
8010                 linePoint(x__, y__);
8011                 if (v__ && v_) { bufferStream.rejoin(); }
8012                 segments.push(bufferStream.result());
8013               }
8014               clipStream.point = point;
8015               if (v_) { activeStream.lineEnd(); }
8016             }
8017
8018             function linePoint(x, y) {
8019               var v = visible(x, y);
8020               if (polygon) { ring.push([x, y]); }
8021               if (first) {
8022                 x__ = x, y__ = y, v__ = v;
8023                 first = false;
8024                 if (v) {
8025                   activeStream.lineStart();
8026                   activeStream.point(x, y);
8027                 }
8028               } else {
8029                 if (v && v_) { activeStream.point(x, y); }
8030                 else {
8031                   var a = [x_ = Math.max(clipMin, Math.min(clipMax, x_)), y_ = Math.max(clipMin, Math.min(clipMax, y_))],
8032                       b = [x = Math.max(clipMin, Math.min(clipMax, x)), y = Math.max(clipMin, Math.min(clipMax, y))];
8033                   if (clipLine(a, b, x0, y0, x1, y1)) {
8034                     if (!v_) {
8035                       activeStream.lineStart();
8036                       activeStream.point(a[0], a[1]);
8037                     }
8038                     activeStream.point(b[0], b[1]);
8039                     if (!v) { activeStream.lineEnd(); }
8040                     clean = false;
8041                   } else if (v) {
8042                     activeStream.lineStart();
8043                     activeStream.point(x, y);
8044                     clean = false;
8045                   }
8046                 }
8047               }
8048               x_ = x, y_ = y, v_ = v;
8049             }
8050
8051             return clipStream;
8052           };
8053         }
8054
8055         var lengthSum = adder(),
8056             lambda0$2,
8057             sinPhi0$1,
8058             cosPhi0$1;
8059
8060         var lengthStream = {
8061           sphere: noop$2,
8062           point: noop$2,
8063           lineStart: lengthLineStart,
8064           lineEnd: noop$2,
8065           polygonStart: noop$2,
8066           polygonEnd: noop$2
8067         };
8068
8069         function lengthLineStart() {
8070           lengthStream.point = lengthPointFirst;
8071           lengthStream.lineEnd = lengthLineEnd;
8072         }
8073
8074         function lengthLineEnd() {
8075           lengthStream.point = lengthStream.lineEnd = noop$2;
8076         }
8077
8078         function lengthPointFirst(lambda, phi) {
8079           lambda *= radians, phi *= radians;
8080           lambda0$2 = lambda, sinPhi0$1 = sin(phi), cosPhi0$1 = cos(phi);
8081           lengthStream.point = lengthPoint;
8082         }
8083
8084         function lengthPoint(lambda, phi) {
8085           lambda *= radians, phi *= radians;
8086           var sinPhi = sin(phi),
8087               cosPhi = cos(phi),
8088               delta = abs$2(lambda - lambda0$2),
8089               cosDelta = cos(delta),
8090               sinDelta = sin(delta),
8091               x = cosPhi * sinDelta,
8092               y = cosPhi0$1 * sinPhi - sinPhi0$1 * cosPhi * cosDelta,
8093               z = sinPhi0$1 * sinPhi + cosPhi0$1 * cosPhi * cosDelta;
8094           lengthSum.add(atan2(sqrt(x * x + y * y), z));
8095           lambda0$2 = lambda, sinPhi0$1 = sinPhi, cosPhi0$1 = cosPhi;
8096         }
8097
8098         function d3_geoLength(object) {
8099           lengthSum.reset();
8100           d3_geoStream(object, lengthStream);
8101           return +lengthSum;
8102         }
8103
8104         function identity(x) {
8105           return x;
8106         }
8107
8108         var areaSum$1 = adder(),
8109             areaRingSum$1 = adder(),
8110             x00,
8111             y00,
8112             x0$1,
8113             y0$1;
8114
8115         var areaStream$1 = {
8116           point: noop$2,
8117           lineStart: noop$2,
8118           lineEnd: noop$2,
8119           polygonStart: function() {
8120             areaStream$1.lineStart = areaRingStart$1;
8121             areaStream$1.lineEnd = areaRingEnd$1;
8122           },
8123           polygonEnd: function() {
8124             areaStream$1.lineStart = areaStream$1.lineEnd = areaStream$1.point = noop$2;
8125             areaSum$1.add(abs$2(areaRingSum$1));
8126             areaRingSum$1.reset();
8127           },
8128           result: function() {
8129             var area = areaSum$1 / 2;
8130             areaSum$1.reset();
8131             return area;
8132           }
8133         };
8134
8135         function areaRingStart$1() {
8136           areaStream$1.point = areaPointFirst$1;
8137         }
8138
8139         function areaPointFirst$1(x, y) {
8140           areaStream$1.point = areaPoint$1;
8141           x00 = x0$1 = x, y00 = y0$1 = y;
8142         }
8143
8144         function areaPoint$1(x, y) {
8145           areaRingSum$1.add(y0$1 * x - x0$1 * y);
8146           x0$1 = x, y0$1 = y;
8147         }
8148
8149         function areaRingEnd$1() {
8150           areaPoint$1(x00, y00);
8151         }
8152
8153         var x0$2 = Infinity,
8154             y0$2 = x0$2,
8155             x1 = -x0$2,
8156             y1 = x1;
8157
8158         var boundsStream$1 = {
8159           point: boundsPoint$1,
8160           lineStart: noop$2,
8161           lineEnd: noop$2,
8162           polygonStart: noop$2,
8163           polygonEnd: noop$2,
8164           result: function() {
8165             var bounds = [[x0$2, y0$2], [x1, y1]];
8166             x1 = y1 = -(y0$2 = x0$2 = Infinity);
8167             return bounds;
8168           }
8169         };
8170
8171         function boundsPoint$1(x, y) {
8172           if (x < x0$2) { x0$2 = x; }
8173           if (x > x1) { x1 = x; }
8174           if (y < y0$2) { y0$2 = y; }
8175           if (y > y1) { y1 = y; }
8176         }
8177
8178         // TODO Enforce positive area for exterior, negative area for interior?
8179
8180         var X0$1 = 0,
8181             Y0$1 = 0,
8182             Z0$1 = 0,
8183             X1$1 = 0,
8184             Y1$1 = 0,
8185             Z1$1 = 0,
8186             X2$1 = 0,
8187             Y2$1 = 0,
8188             Z2$1 = 0,
8189             x00$1,
8190             y00$1,
8191             x0$3,
8192             y0$3;
8193
8194         var centroidStream$1 = {
8195           point: centroidPoint$1,
8196           lineStart: centroidLineStart$1,
8197           lineEnd: centroidLineEnd$1,
8198           polygonStart: function() {
8199             centroidStream$1.lineStart = centroidRingStart$1;
8200             centroidStream$1.lineEnd = centroidRingEnd$1;
8201           },
8202           polygonEnd: function() {
8203             centroidStream$1.point = centroidPoint$1;
8204             centroidStream$1.lineStart = centroidLineStart$1;
8205             centroidStream$1.lineEnd = centroidLineEnd$1;
8206           },
8207           result: function() {
8208             var centroid = Z2$1 ? [X2$1 / Z2$1, Y2$1 / Z2$1]
8209                 : Z1$1 ? [X1$1 / Z1$1, Y1$1 / Z1$1]
8210                 : Z0$1 ? [X0$1 / Z0$1, Y0$1 / Z0$1]
8211                 : [NaN, NaN];
8212             X0$1 = Y0$1 = Z0$1 =
8213             X1$1 = Y1$1 = Z1$1 =
8214             X2$1 = Y2$1 = Z2$1 = 0;
8215             return centroid;
8216           }
8217         };
8218
8219         function centroidPoint$1(x, y) {
8220           X0$1 += x;
8221           Y0$1 += y;
8222           ++Z0$1;
8223         }
8224
8225         function centroidLineStart$1() {
8226           centroidStream$1.point = centroidPointFirstLine;
8227         }
8228
8229         function centroidPointFirstLine(x, y) {
8230           centroidStream$1.point = centroidPointLine;
8231           centroidPoint$1(x0$3 = x, y0$3 = y);
8232         }
8233
8234         function centroidPointLine(x, y) {
8235           var dx = x - x0$3, dy = y - y0$3, z = sqrt(dx * dx + dy * dy);
8236           X1$1 += z * (x0$3 + x) / 2;
8237           Y1$1 += z * (y0$3 + y) / 2;
8238           Z1$1 += z;
8239           centroidPoint$1(x0$3 = x, y0$3 = y);
8240         }
8241
8242         function centroidLineEnd$1() {
8243           centroidStream$1.point = centroidPoint$1;
8244         }
8245
8246         function centroidRingStart$1() {
8247           centroidStream$1.point = centroidPointFirstRing;
8248         }
8249
8250         function centroidRingEnd$1() {
8251           centroidPointRing(x00$1, y00$1);
8252         }
8253
8254         function centroidPointFirstRing(x, y) {
8255           centroidStream$1.point = centroidPointRing;
8256           centroidPoint$1(x00$1 = x0$3 = x, y00$1 = y0$3 = y);
8257         }
8258
8259         function centroidPointRing(x, y) {
8260           var dx = x - x0$3,
8261               dy = y - y0$3,
8262               z = sqrt(dx * dx + dy * dy);
8263
8264           X1$1 += z * (x0$3 + x) / 2;
8265           Y1$1 += z * (y0$3 + y) / 2;
8266           Z1$1 += z;
8267
8268           z = y0$3 * x - x0$3 * y;
8269           X2$1 += z * (x0$3 + x);
8270           Y2$1 += z * (y0$3 + y);
8271           Z2$1 += z * 3;
8272           centroidPoint$1(x0$3 = x, y0$3 = y);
8273         }
8274
8275         function PathContext(context) {
8276           this._context = context;
8277         }
8278
8279         PathContext.prototype = {
8280           _radius: 4.5,
8281           pointRadius: function(_) {
8282             return this._radius = _, this;
8283           },
8284           polygonStart: function() {
8285             this._line = 0;
8286           },
8287           polygonEnd: function() {
8288             this._line = NaN;
8289           },
8290           lineStart: function() {
8291             this._point = 0;
8292           },
8293           lineEnd: function() {
8294             if (this._line === 0) { this._context.closePath(); }
8295             this._point = NaN;
8296           },
8297           point: function(x, y) {
8298             switch (this._point) {
8299               case 0: {
8300                 this._context.moveTo(x, y);
8301                 this._point = 1;
8302                 break;
8303               }
8304               case 1: {
8305                 this._context.lineTo(x, y);
8306                 break;
8307               }
8308               default: {
8309                 this._context.moveTo(x + this._radius, y);
8310                 this._context.arc(x, y, this._radius, 0, tau);
8311                 break;
8312               }
8313             }
8314           },
8315           result: noop$2
8316         };
8317
8318         var lengthSum$1 = adder(),
8319             lengthRing,
8320             x00$2,
8321             y00$2,
8322             x0$4,
8323             y0$4;
8324
8325         var lengthStream$1 = {
8326           point: noop$2,
8327           lineStart: function() {
8328             lengthStream$1.point = lengthPointFirst$1;
8329           },
8330           lineEnd: function() {
8331             if (lengthRing) { lengthPoint$1(x00$2, y00$2); }
8332             lengthStream$1.point = noop$2;
8333           },
8334           polygonStart: function() {
8335             lengthRing = true;
8336           },
8337           polygonEnd: function() {
8338             lengthRing = null;
8339           },
8340           result: function() {
8341             var length = +lengthSum$1;
8342             lengthSum$1.reset();
8343             return length;
8344           }
8345         };
8346
8347         function lengthPointFirst$1(x, y) {
8348           lengthStream$1.point = lengthPoint$1;
8349           x00$2 = x0$4 = x, y00$2 = y0$4 = y;
8350         }
8351
8352         function lengthPoint$1(x, y) {
8353           x0$4 -= x, y0$4 -= y;
8354           lengthSum$1.add(sqrt(x0$4 * x0$4 + y0$4 * y0$4));
8355           x0$4 = x, y0$4 = y;
8356         }
8357
8358         function PathString() {
8359           this._string = [];
8360         }
8361
8362         PathString.prototype = {
8363           _radius: 4.5,
8364           _circle: circle(4.5),
8365           pointRadius: function(_) {
8366             if ((_ = +_) !== this._radius) { this._radius = _, this._circle = null; }
8367             return this;
8368           },
8369           polygonStart: function() {
8370             this._line = 0;
8371           },
8372           polygonEnd: function() {
8373             this._line = NaN;
8374           },
8375           lineStart: function() {
8376             this._point = 0;
8377           },
8378           lineEnd: function() {
8379             if (this._line === 0) { this._string.push("Z"); }
8380             this._point = NaN;
8381           },
8382           point: function(x, y) {
8383             switch (this._point) {
8384               case 0: {
8385                 this._string.push("M", x, ",", y);
8386                 this._point = 1;
8387                 break;
8388               }
8389               case 1: {
8390                 this._string.push("L", x, ",", y);
8391                 break;
8392               }
8393               default: {
8394                 if (this._circle == null) { this._circle = circle(this._radius); }
8395                 this._string.push("M", x, ",", y, this._circle);
8396                 break;
8397               }
8398             }
8399           },
8400           result: function() {
8401             if (this._string.length) {
8402               var result = this._string.join("");
8403               this._string = [];
8404               return result;
8405             } else {
8406               return null;
8407             }
8408           }
8409         };
8410
8411         function circle(radius) {
8412           return "m0," + radius
8413               + "a" + radius + "," + radius + " 0 1,1 0," + -2 * radius
8414               + "a" + radius + "," + radius + " 0 1,1 0," + 2 * radius
8415               + "z";
8416         }
8417
8418         function d3_geoPath(projection, context) {
8419           var pointRadius = 4.5,
8420               projectionStream,
8421               contextStream;
8422
8423           function path(object) {
8424             if (object) {
8425               if (typeof pointRadius === "function") { contextStream.pointRadius(+pointRadius.apply(this, arguments)); }
8426               d3_geoStream(object, projectionStream(contextStream));
8427             }
8428             return contextStream.result();
8429           }
8430
8431           path.area = function(object) {
8432             d3_geoStream(object, projectionStream(areaStream$1));
8433             return areaStream$1.result();
8434           };
8435
8436           path.measure = function(object) {
8437             d3_geoStream(object, projectionStream(lengthStream$1));
8438             return lengthStream$1.result();
8439           };
8440
8441           path.bounds = function(object) {
8442             d3_geoStream(object, projectionStream(boundsStream$1));
8443             return boundsStream$1.result();
8444           };
8445
8446           path.centroid = function(object) {
8447             d3_geoStream(object, projectionStream(centroidStream$1));
8448             return centroidStream$1.result();
8449           };
8450
8451           path.projection = function(_) {
8452             return arguments.length ? (projectionStream = _ == null ? (projection = null, identity) : (projection = _).stream, path) : projection;
8453           };
8454
8455           path.context = function(_) {
8456             if (!arguments.length) { return context; }
8457             contextStream = _ == null ? (context = null, new PathString) : new PathContext(context = _);
8458             if (typeof pointRadius !== "function") { contextStream.pointRadius(pointRadius); }
8459             return path;
8460           };
8461
8462           path.pointRadius = function(_) {
8463             if (!arguments.length) { return pointRadius; }
8464             pointRadius = typeof _ === "function" ? _ : (contextStream.pointRadius(+_), +_);
8465             return path;
8466           };
8467
8468           return path.projection(projection).context(context);
8469         }
8470
8471         function d3_geoTransform(methods) {
8472           return {
8473             stream: transformer(methods)
8474           };
8475         }
8476
8477         function transformer(methods) {
8478           return function(stream) {
8479             var s = new TransformStream;
8480             for (var key in methods) { s[key] = methods[key]; }
8481             s.stream = stream;
8482             return s;
8483           };
8484         }
8485
8486         function TransformStream() {}
8487
8488         TransformStream.prototype = {
8489           constructor: TransformStream,
8490           point: function(x, y) { this.stream.point(x, y); },
8491           sphere: function() { this.stream.sphere(); },
8492           lineStart: function() { this.stream.lineStart(); },
8493           lineEnd: function() { this.stream.lineEnd(); },
8494           polygonStart: function() { this.stream.polygonStart(); },
8495           polygonEnd: function() { this.stream.polygonEnd(); }
8496         };
8497
8498         function fit(projection, fitBounds, object) {
8499           var clip = projection.clipExtent && projection.clipExtent();
8500           projection.scale(150).translate([0, 0]);
8501           if (clip != null) { projection.clipExtent(null); }
8502           d3_geoStream(object, projection.stream(boundsStream$1));
8503           fitBounds(boundsStream$1.result());
8504           if (clip != null) { projection.clipExtent(clip); }
8505           return projection;
8506         }
8507
8508         function fitExtent(projection, extent, object) {
8509           return fit(projection, function(b) {
8510             var w = extent[1][0] - extent[0][0],
8511                 h = extent[1][1] - extent[0][1],
8512                 k = Math.min(w / (b[1][0] - b[0][0]), h / (b[1][1] - b[0][1])),
8513                 x = +extent[0][0] + (w - k * (b[1][0] + b[0][0])) / 2,
8514                 y = +extent[0][1] + (h - k * (b[1][1] + b[0][1])) / 2;
8515             projection.scale(150 * k).translate([x, y]);
8516           }, object);
8517         }
8518
8519         function fitSize(projection, size, object) {
8520           return fitExtent(projection, [[0, 0], size], object);
8521         }
8522
8523         function fitWidth(projection, width, object) {
8524           return fit(projection, function(b) {
8525             var w = +width,
8526                 k = w / (b[1][0] - b[0][0]),
8527                 x = (w - k * (b[1][0] + b[0][0])) / 2,
8528                 y = -k * b[0][1];
8529             projection.scale(150 * k).translate([x, y]);
8530           }, object);
8531         }
8532
8533         function fitHeight(projection, height, object) {
8534           return fit(projection, function(b) {
8535             var h = +height,
8536                 k = h / (b[1][1] - b[0][1]),
8537                 x = -k * b[0][0],
8538                 y = (h - k * (b[1][1] + b[0][1])) / 2;
8539             projection.scale(150 * k).translate([x, y]);
8540           }, object);
8541         }
8542
8543         var maxDepth = 16, // maximum depth of subdivision
8544             cosMinDistance = cos(30 * radians); // cos(minimum angular distance)
8545
8546         function resample(project, delta2) {
8547           return +delta2 ? resample$1(project, delta2) : resampleNone(project);
8548         }
8549
8550         function resampleNone(project) {
8551           return transformer({
8552             point: function(x, y) {
8553               x = project(x, y);
8554               this.stream.point(x[0], x[1]);
8555             }
8556           });
8557         }
8558
8559         function resample$1(project, delta2) {
8560
8561           function resampleLineTo(x0, y0, lambda0, a0, b0, c0, x1, y1, lambda1, a1, b1, c1, depth, stream) {
8562             var dx = x1 - x0,
8563                 dy = y1 - y0,
8564                 d2 = dx * dx + dy * dy;
8565             if (d2 > 4 * delta2 && depth--) {
8566               var a = a0 + a1,
8567                   b = b0 + b1,
8568                   c = c0 + c1,
8569                   m = sqrt(a * a + b * b + c * c),
8570                   phi2 = asin(c /= m),
8571                   lambda2 = abs$2(abs$2(c) - 1) < epsilon || abs$2(lambda0 - lambda1) < epsilon ? (lambda0 + lambda1) / 2 : atan2(b, a),
8572                   p = project(lambda2, phi2),
8573                   x2 = p[0],
8574                   y2 = p[1],
8575                   dx2 = x2 - x0,
8576                   dy2 = y2 - y0,
8577                   dz = dy * dx2 - dx * dy2;
8578               if (dz * dz / d2 > delta2 // perpendicular projected distance
8579                   || abs$2((dx * dx2 + dy * dy2) / d2 - 0.5) > 0.3 // midpoint close to an end
8580                   || a0 * a1 + b0 * b1 + c0 * c1 < cosMinDistance) { // angular distance
8581                 resampleLineTo(x0, y0, lambda0, a0, b0, c0, x2, y2, lambda2, a /= m, b /= m, c, depth, stream);
8582                 stream.point(x2, y2);
8583                 resampleLineTo(x2, y2, lambda2, a, b, c, x1, y1, lambda1, a1, b1, c1, depth, stream);
8584               }
8585             }
8586           }
8587           return function(stream) {
8588             var lambda00, x00, y00, a00, b00, c00, // first point
8589                 lambda0, x0, y0, a0, b0, c0; // previous point
8590
8591             var resampleStream = {
8592               point: point,
8593               lineStart: lineStart,
8594               lineEnd: lineEnd,
8595               polygonStart: function() { stream.polygonStart(); resampleStream.lineStart = ringStart; },
8596               polygonEnd: function() { stream.polygonEnd(); resampleStream.lineStart = lineStart; }
8597             };
8598
8599             function point(x, y) {
8600               x = project(x, y);
8601               stream.point(x[0], x[1]);
8602             }
8603
8604             function lineStart() {
8605               x0 = NaN;
8606               resampleStream.point = linePoint;
8607               stream.lineStart();
8608             }
8609
8610             function linePoint(lambda, phi) {
8611               var c = cartesian([lambda, phi]), p = project(lambda, phi);
8612               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);
8613               stream.point(x0, y0);
8614             }
8615
8616             function lineEnd() {
8617               resampleStream.point = point;
8618               stream.lineEnd();
8619             }
8620
8621             function ringStart() {
8622               lineStart();
8623               resampleStream.point = ringPoint;
8624               resampleStream.lineEnd = ringEnd;
8625             }
8626
8627             function ringPoint(lambda, phi) {
8628               linePoint(lambda00 = lambda, phi), x00 = x0, y00 = y0, a00 = a0, b00 = b0, c00 = c0;
8629               resampleStream.point = linePoint;
8630             }
8631
8632             function ringEnd() {
8633               resampleLineTo(x0, y0, lambda0, a0, b0, c0, x00, y00, lambda00, a00, b00, c00, maxDepth, stream);
8634               resampleStream.lineEnd = lineEnd;
8635               lineEnd();
8636             }
8637
8638             return resampleStream;
8639           };
8640         }
8641
8642         var transformRadians = transformer({
8643           point: function(x, y) {
8644             this.stream.point(x * radians, y * radians);
8645           }
8646         });
8647
8648         function transformRotate(rotate) {
8649           return transformer({
8650             point: function(x, y) {
8651               var r = rotate(x, y);
8652               return this.stream.point(r[0], r[1]);
8653             }
8654           });
8655         }
8656
8657         function scaleTranslate(k, dx, dy, sx, sy) {
8658           function transform(x, y) {
8659             x *= sx; y *= sy;
8660             return [dx + k * x, dy - k * y];
8661           }
8662           transform.invert = function(x, y) {
8663             return [(x - dx) / k * sx, (dy - y) / k * sy];
8664           };
8665           return transform;
8666         }
8667
8668         function scaleTranslateRotate(k, dx, dy, sx, sy, alpha) {
8669           var cosAlpha = cos(alpha),
8670               sinAlpha = sin(alpha),
8671               a = cosAlpha * k,
8672               b = sinAlpha * k,
8673               ai = cosAlpha / k,
8674               bi = sinAlpha / k,
8675               ci = (sinAlpha * dy - cosAlpha * dx) / k,
8676               fi = (sinAlpha * dx + cosAlpha * dy) / k;
8677           function transform(x, y) {
8678             x *= sx; y *= sy;
8679             return [a * x - b * y + dx, dy - b * x - a * y];
8680           }
8681           transform.invert = function(x, y) {
8682             return [sx * (ai * x - bi * y + ci), sy * (fi - bi * x - ai * y)];
8683           };
8684           return transform;
8685         }
8686
8687         function projection(project) {
8688           return projectionMutator(function() { return project; })();
8689         }
8690
8691         function projectionMutator(projectAt) {
8692           var project,
8693               k = 150, // scale
8694               x = 480, y = 250, // translate
8695               lambda = 0, phi = 0, // center
8696               deltaLambda = 0, deltaPhi = 0, deltaGamma = 0, rotate, // pre-rotate
8697               alpha = 0, // post-rotate angle
8698               sx = 1, // reflectX
8699               sy = 1, // reflectX
8700               theta = null, preclip = clipAntimeridian, // pre-clip angle
8701               x0 = null, y0, x1, y1, postclip = identity, // post-clip extent
8702               delta2 = 0.5, // precision
8703               projectResample,
8704               projectTransform,
8705               projectRotateTransform,
8706               cache,
8707               cacheStream;
8708
8709           function projection(point) {
8710             return projectRotateTransform(point[0] * radians, point[1] * radians);
8711           }
8712
8713           function invert(point) {
8714             point = projectRotateTransform.invert(point[0], point[1]);
8715             return point && [point[0] * degrees, point[1] * degrees];
8716           }
8717
8718           projection.stream = function(stream) {
8719             return cache && cacheStream === stream ? cache : cache = transformRadians(transformRotate(rotate)(preclip(projectResample(postclip(cacheStream = stream)))));
8720           };
8721
8722           projection.preclip = function(_) {
8723             return arguments.length ? (preclip = _, theta = undefined, reset()) : preclip;
8724           };
8725
8726           projection.postclip = function(_) {
8727             return arguments.length ? (postclip = _, x0 = y0 = x1 = y1 = null, reset()) : postclip;
8728           };
8729
8730           projection.clipAngle = function(_) {
8731             return arguments.length ? (preclip = +_ ? clipCircle(theta = _ * radians) : (theta = null, clipAntimeridian), reset()) : theta * degrees;
8732           };
8733
8734           projection.clipExtent = function(_) {
8735             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]];
8736           };
8737
8738           projection.scale = function(_) {
8739             return arguments.length ? (k = +_, recenter()) : k;
8740           };
8741
8742           projection.translate = function(_) {
8743             return arguments.length ? (x = +_[0], y = +_[1], recenter()) : [x, y];
8744           };
8745
8746           projection.center = function(_) {
8747             return arguments.length ? (lambda = _[0] % 360 * radians, phi = _[1] % 360 * radians, recenter()) : [lambda * degrees, phi * degrees];
8748           };
8749
8750           projection.rotate = function(_) {
8751             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];
8752           };
8753
8754           projection.angle = function(_) {
8755             return arguments.length ? (alpha = _ % 360 * radians, recenter()) : alpha * degrees;
8756           };
8757
8758           projection.reflectX = function(_) {
8759             return arguments.length ? (sx = _ ? -1 : 1, recenter()) : sx < 0;
8760           };
8761
8762           projection.reflectY = function(_) {
8763             return arguments.length ? (sy = _ ? -1 : 1, recenter()) : sy < 0;
8764           };
8765
8766           projection.precision = function(_) {
8767             return arguments.length ? (projectResample = resample(projectTransform, delta2 = _ * _), reset()) : sqrt(delta2);
8768           };
8769
8770           projection.fitExtent = function(extent, object) {
8771             return fitExtent(projection, extent, object);
8772           };
8773
8774           projection.fitSize = function(size, object) {
8775             return fitSize(projection, size, object);
8776           };
8777
8778           projection.fitWidth = function(width, object) {
8779             return fitWidth(projection, width, object);
8780           };
8781
8782           projection.fitHeight = function(height, object) {
8783             return fitHeight(projection, height, object);
8784           };
8785
8786           function recenter() {
8787             var center = scaleTranslateRotate(k, 0, 0, sx, sy, alpha).apply(null, project(lambda, phi)),
8788                 transform = (alpha ? scaleTranslateRotate : scaleTranslate)(k, x - center[0], y - center[1], sx, sy, alpha);
8789             rotate = rotateRadians(deltaLambda, deltaPhi, deltaGamma);
8790             projectTransform = compose(project, transform);
8791             projectRotateTransform = compose(rotate, projectTransform);
8792             projectResample = resample(projectTransform, delta2);
8793             return reset();
8794           }
8795
8796           function reset() {
8797             cache = cacheStream = null;
8798             return projection;
8799           }
8800
8801           return function() {
8802             project = projectAt.apply(this, arguments);
8803             projection.invert = project.invert && invert;
8804             return recenter();
8805           };
8806         }
8807
8808         function mercatorRaw(lambda, phi) {
8809           return [lambda, log(tan((halfPi + phi) / 2))];
8810         }
8811
8812         mercatorRaw.invert = function(x, y) {
8813           return [x, 2 * atan(exp(y)) - halfPi];
8814         };
8815
8816         function mercator() {
8817           return mercatorProjection(mercatorRaw)
8818               .scale(961 / tau);
8819         }
8820
8821         function mercatorProjection(project) {
8822           var m = projection(project),
8823               center = m.center,
8824               scale = m.scale,
8825               translate = m.translate,
8826               clipExtent = m.clipExtent,
8827               x0 = null, y0, x1, y1; // clip extent
8828
8829           m.scale = function(_) {
8830             return arguments.length ? (scale(_), reclip()) : scale();
8831           };
8832
8833           m.translate = function(_) {
8834             return arguments.length ? (translate(_), reclip()) : translate();
8835           };
8836
8837           m.center = function(_) {
8838             return arguments.length ? (center(_), reclip()) : center();
8839           };
8840
8841           m.clipExtent = function(_) {
8842             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]];
8843           };
8844
8845           function reclip() {
8846             var k = pi * scale(),
8847                 t = m(rotation(m.rotate()).invert([0, 0]));
8848             return clipExtent(x0 == null
8849                 ? [[t[0] - k, t[1] - k], [t[0] + k, t[1] + k]] : project === mercatorRaw
8850                 ? [[Math.max(t[0] - k, x0), y0], [Math.min(t[0] + k, x1), y1]]
8851                 : [[x0, Math.max(t[1] - k, y0)], [x1, Math.min(t[1] + k, y1)]]);
8852           }
8853
8854           return reclip();
8855         }
8856
8857         function d3_geoIdentity() {
8858           var k = 1, tx = 0, ty = 0, sx = 1, sy = 1, // scale, translate and reflect
8859               alpha = 0, ca, sa, // angle
8860               x0 = null, y0, x1, y1, // clip extent
8861               kx = 1, ky = 1,
8862               transform = transformer({
8863                 point: function(x, y) {
8864                   var p = projection([x, y]);
8865                   this.stream.point(p[0], p[1]);
8866                 }
8867               }),
8868               postclip = identity,
8869               cache,
8870               cacheStream;
8871
8872           function reset() {
8873             kx = k * sx;
8874             ky = k * sy;
8875             cache = cacheStream = null;
8876             return projection;
8877           }
8878
8879           function projection (p) {
8880             var x = p[0] * kx, y = p[1] * ky;
8881             if (alpha) {
8882               var t = y * ca - x * sa;
8883               x = x * ca + y * sa;
8884               y = t;
8885             }    
8886             return [x + tx, y + ty];
8887           }
8888           projection.invert = function(p) {
8889             var x = p[0] - tx, y = p[1] - ty;
8890             if (alpha) {
8891               var t = y * ca + x * sa;
8892               x = x * ca - y * sa;
8893               y = t;
8894             }
8895             return [x / kx, y / ky];
8896           };
8897           projection.stream = function(stream) {
8898             return cache && cacheStream === stream ? cache : cache = transform(postclip(cacheStream = stream));
8899           };
8900           projection.postclip = function(_) {
8901             return arguments.length ? (postclip = _, x0 = y0 = x1 = y1 = null, reset()) : postclip;
8902           };
8903           projection.clipExtent = function(_) {
8904             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]];
8905           };
8906           projection.scale = function(_) {
8907             return arguments.length ? (k = +_, reset()) : k;
8908           };
8909           projection.translate = function(_) {
8910             return arguments.length ? (tx = +_[0], ty = +_[1], reset()) : [tx, ty];
8911           };
8912           projection.angle = function(_) {
8913             return arguments.length ? (alpha = _ % 360 * radians, sa = sin(alpha), ca = cos(alpha), reset()) : alpha * degrees;
8914           };
8915           projection.reflectX = function(_) {
8916             return arguments.length ? (sx = _ ? -1 : 1, reset()) : sx < 0;
8917           };
8918           projection.reflectY = function(_) {
8919             return arguments.length ? (sy = _ ? -1 : 1, reset()) : sy < 0;
8920           };
8921           projection.fitExtent = function(extent, object) {
8922             return fitExtent(projection, extent, object);
8923           };
8924           projection.fitSize = function(size, object) {
8925             return fitSize(projection, size, object);
8926           };
8927           projection.fitWidth = function(width, object) {
8928             return fitWidth(projection, width, object);
8929           };
8930           projection.fitHeight = function(height, object) {
8931             return fitHeight(projection, height, object);
8932           };
8933
8934           return projection;
8935         }
8936
8937         // constants
8938         var TAU = 2 * Math.PI;
8939         var EQUATORIAL_RADIUS = 6356752.314245179;
8940         var POLAR_RADIUS = 6378137.0;
8941
8942
8943         function geoLatToMeters(dLat) {
8944             return dLat * (TAU * POLAR_RADIUS / 360);
8945         }
8946
8947
8948         function geoLonToMeters(dLon, atLat) {
8949             return Math.abs(atLat) >= 90 ? 0 :
8950                 dLon * (TAU * EQUATORIAL_RADIUS / 360) * Math.abs(Math.cos(atLat * (Math.PI / 180)));
8951         }
8952
8953
8954         function geoMetersToLat(m) {
8955             return m / (TAU * POLAR_RADIUS / 360);
8956         }
8957
8958
8959         function geoMetersToLon(m, atLat) {
8960             return Math.abs(atLat) >= 90 ? 0 :
8961                 m / (TAU * EQUATORIAL_RADIUS / 360) / Math.abs(Math.cos(atLat * (Math.PI / 180)));
8962         }
8963
8964
8965         function geoMetersToOffset(meters, tileSize) {
8966             tileSize = tileSize || 256;
8967             return [
8968                 meters[0] * tileSize / (TAU * EQUATORIAL_RADIUS),
8969                 -meters[1] * tileSize / (TAU * POLAR_RADIUS)
8970             ];
8971         }
8972
8973
8974         function geoOffsetToMeters(offset, tileSize) {
8975             tileSize = tileSize || 256;
8976             return [
8977                 offset[0] * TAU * EQUATORIAL_RADIUS / tileSize,
8978                 -offset[1] * TAU * POLAR_RADIUS / tileSize
8979             ];
8980         }
8981
8982
8983         // Equirectangular approximation of spherical distances on Earth
8984         function geoSphericalDistance(a, b) {
8985             var x = geoLonToMeters(a[0] - b[0], (a[1] + b[1]) / 2);
8986             var y = geoLatToMeters(a[1] - b[1]);
8987             return Math.sqrt((x * x) + (y * y));
8988         }
8989
8990
8991         // scale to zoom
8992         function geoScaleToZoom(k, tileSize) {
8993             tileSize = tileSize || 256;
8994             var log2ts = Math.log(tileSize) * Math.LOG2E;
8995             return Math.log(k * TAU) / Math.LN2 - log2ts;
8996         }
8997
8998
8999         // zoom to scale
9000         function geoZoomToScale(z, tileSize) {
9001             tileSize = tileSize || 256;
9002             return tileSize * Math.pow(2, z) / TAU;
9003         }
9004
9005
9006         // returns info about the node from `nodes` closest to the given `point`
9007         function geoSphericalClosestNode(nodes, point) {
9008             var minDistance = Infinity, distance;
9009             var indexOfMin;
9010
9011             for (var i in nodes) {
9012                 distance = geoSphericalDistance(nodes[i].loc, point);
9013                 if (distance < minDistance) {
9014                     minDistance = distance;
9015                     indexOfMin = i;
9016                 }
9017             }
9018
9019             if (indexOfMin !== undefined) {
9020                 return { index: indexOfMin, distance: minDistance, node: nodes[indexOfMin] };
9021             } else {
9022                 return null;
9023             }
9024         }
9025
9026         function geoExtent(min, max) {
9027             if (!(this instanceof geoExtent)) {
9028                 return new geoExtent(min, max);
9029             } else if (min instanceof geoExtent) {
9030                 return min;
9031             } else if (min && min.length === 2 && min[0].length === 2 && min[1].length === 2) {
9032                 this[0] = min[0];
9033                 this[1] = min[1];
9034             } else {
9035                 this[0] = min        || [ Infinity,  Infinity];
9036                 this[1] = max || min || [-Infinity, -Infinity];
9037             }
9038         }
9039
9040         geoExtent.prototype = new Array(2);
9041
9042         Object.assign(geoExtent.prototype, {
9043
9044             equals: function (obj) {
9045                 return this[0][0] === obj[0][0] &&
9046                     this[0][1] === obj[0][1] &&
9047                     this[1][0] === obj[1][0] &&
9048                     this[1][1] === obj[1][1];
9049             },
9050
9051
9052             extend: function(obj) {
9053                 if (!(obj instanceof geoExtent)) { obj = new geoExtent(obj); }
9054                 return geoExtent(
9055                     [Math.min(obj[0][0], this[0][0]), Math.min(obj[0][1], this[0][1])],
9056                     [Math.max(obj[1][0], this[1][0]), Math.max(obj[1][1], this[1][1])]
9057                 );
9058             },
9059
9060
9061             _extend: function(extent) {
9062                 this[0][0] = Math.min(extent[0][0], this[0][0]);
9063                 this[0][1] = Math.min(extent[0][1], this[0][1]);
9064                 this[1][0] = Math.max(extent[1][0], this[1][0]);
9065                 this[1][1] = Math.max(extent[1][1], this[1][1]);
9066             },
9067
9068
9069             area: function() {
9070                 return Math.abs((this[1][0] - this[0][0]) * (this[1][1] - this[0][1]));
9071             },
9072
9073
9074             center: function() {
9075                 return [(this[0][0] + this[1][0]) / 2, (this[0][1] + this[1][1]) / 2];
9076             },
9077
9078
9079             rectangle: function() {
9080                 return [this[0][0], this[0][1], this[1][0], this[1][1]];
9081             },
9082
9083
9084             bbox: function() {
9085                 return { minX: this[0][0], minY: this[0][1], maxX: this[1][0], maxY: this[1][1] };
9086             },
9087
9088
9089             polygon: function() {
9090                 return [
9091                     [this[0][0], this[0][1]],
9092                     [this[0][0], this[1][1]],
9093                     [this[1][0], this[1][1]],
9094                     [this[1][0], this[0][1]],
9095                     [this[0][0], this[0][1]]
9096                 ];
9097             },
9098
9099
9100             contains: function(obj) {
9101                 if (!(obj instanceof geoExtent)) { obj = new geoExtent(obj); }
9102                 return obj[0][0] >= this[0][0] &&
9103                        obj[0][1] >= this[0][1] &&
9104                        obj[1][0] <= this[1][0] &&
9105                        obj[1][1] <= this[1][1];
9106             },
9107
9108
9109             intersects: function(obj) {
9110                 if (!(obj instanceof geoExtent)) { obj = new geoExtent(obj); }
9111                 return obj[0][0] <= this[1][0] &&
9112                        obj[0][1] <= this[1][1] &&
9113                        obj[1][0] >= this[0][0] &&
9114                        obj[1][1] >= this[0][1];
9115             },
9116
9117
9118             intersection: function(obj) {
9119                 if (!this.intersects(obj)) { return new geoExtent(); }
9120                 return new geoExtent(
9121                     [Math.max(obj[0][0], this[0][0]), Math.max(obj[0][1], this[0][1])],
9122                     [Math.min(obj[1][0], this[1][0]), Math.min(obj[1][1], this[1][1])]
9123                 );
9124             },
9125
9126
9127             percentContainedIn: function(obj) {
9128                 if (!(obj instanceof geoExtent)) { obj = new geoExtent(obj); }
9129                 var a1 = this.intersection(obj).area();
9130                 var a2 = this.area();
9131
9132                 if (a1 === Infinity || a2 === Infinity) {
9133                     return 0;
9134                 } else if (a1 === 0 || a2 === 0) {
9135                     if (obj.contains(this)) {
9136                         return 1;
9137                     }
9138                     return 0;
9139                 } else {
9140                     return a1 / a2;
9141                 }
9142             },
9143
9144
9145             padByMeters: function(meters) {
9146                 var dLat = geoMetersToLat(meters);
9147                 var dLon = geoMetersToLon(meters, this.center()[1]);
9148                 return geoExtent(
9149                     [this[0][0] - dLon, this[0][1] - dLat],
9150                     [this[1][0] + dLon, this[1][1] + dLat]
9151                 );
9152             },
9153
9154
9155             toParam: function() {
9156                 return this.rectangle().join(',');
9157             }
9158
9159         });
9160
9161         function d3_polygonArea(polygon) {
9162           var i = -1,
9163               n = polygon.length,
9164               a,
9165               b = polygon[n - 1],
9166               area = 0;
9167
9168           while (++i < n) {
9169             a = b;
9170             b = polygon[i];
9171             area += a[1] * b[0] - a[0] * b[1];
9172           }
9173
9174           return area / 2;
9175         }
9176
9177         function d3_polygonCentroid(polygon) {
9178           var i = -1,
9179               n = polygon.length,
9180               x = 0,
9181               y = 0,
9182               a,
9183               b = polygon[n - 1],
9184               c,
9185               k = 0;
9186
9187           while (++i < n) {
9188             a = b;
9189             b = polygon[i];
9190             k += c = a[0] * b[1] - b[0] * a[1];
9191             x += (a[0] + b[0]) * c;
9192             y += (a[1] + b[1]) * c;
9193           }
9194
9195           return k *= 3, [x / k, y / k];
9196         }
9197
9198         // Returns the 2D cross product of AB and AC vectors, i.e., the z-component of
9199         // the 3D cross product in a quadrant I Cartesian coordinate system (+x is
9200         // right, +y is up). Returns a positive value if ABC is counter-clockwise,
9201         // negative if clockwise, and zero if the points are collinear.
9202         function cross(a, b, c) {
9203           return (b[0] - a[0]) * (c[1] - a[1]) - (b[1] - a[1]) * (c[0] - a[0]);
9204         }
9205
9206         function lexicographicOrder(a, b) {
9207           return a[0] - b[0] || a[1] - b[1];
9208         }
9209
9210         // Computes the upper convex hull per the monotone chain algorithm.
9211         // Assumes points.length >= 3, is sorted by x, unique in y.
9212         // Returns an array of indices into points in left-to-right order.
9213         function computeUpperHullIndexes(points) {
9214           var n = points.length,
9215               indexes = [0, 1],
9216               size = 2;
9217
9218           for (var i = 2; i < n; ++i) {
9219             while (size > 1 && cross(points[indexes[size - 2]], points[indexes[size - 1]], points[i]) <= 0) { --size; }
9220             indexes[size++] = i;
9221           }
9222
9223           return indexes.slice(0, size); // remove popped points
9224         }
9225
9226         function d3_polygonHull(points) {
9227           if ((n = points.length) < 3) { return null; }
9228
9229           var i,
9230               n,
9231               sortedPoints = new Array(n),
9232               flippedPoints = new Array(n);
9233
9234           for (i = 0; i < n; ++i) { sortedPoints[i] = [+points[i][0], +points[i][1], i]; }
9235           sortedPoints.sort(lexicographicOrder);
9236           for (i = 0; i < n; ++i) { flippedPoints[i] = [sortedPoints[i][0], -sortedPoints[i][1]]; }
9237
9238           var upperIndexes = computeUpperHullIndexes(sortedPoints),
9239               lowerIndexes = computeUpperHullIndexes(flippedPoints);
9240
9241           // Construct the hull polygon, removing possible duplicate endpoints.
9242           var skipLeft = lowerIndexes[0] === upperIndexes[0],
9243               skipRight = lowerIndexes[lowerIndexes.length - 1] === upperIndexes[upperIndexes.length - 1],
9244               hull = [];
9245
9246           // Add upper hull in right-to-l order.
9247           // Then add lower hull in left-to-right order.
9248           for (i = upperIndexes.length - 1; i >= 0; --i) { hull.push(points[sortedPoints[upperIndexes[i]][2]]); }
9249           for (i = +skipLeft; i < lowerIndexes.length - skipRight; ++i) { hull.push(points[sortedPoints[lowerIndexes[i]][2]]); }
9250
9251           return hull;
9252         }
9253
9254         // vector equals
9255         function geoVecEqual(a, b, epsilon) {
9256             if (epsilon) {
9257                 return (Math.abs(a[0] - b[0]) <= epsilon) && (Math.abs(a[1] - b[1]) <= epsilon);
9258             } else {
9259                 return (a[0] === b[0]) && (a[1] === b[1]);
9260             }
9261         }
9262
9263         // vector addition
9264         function geoVecAdd(a, b) {
9265             return [ a[0] + b[0], a[1] + b[1] ];
9266         }
9267
9268         // vector subtraction
9269         function geoVecSubtract(a, b) {
9270             return [ a[0] - b[0], a[1] - b[1] ];
9271         }
9272
9273         // vector scaling
9274         function geoVecScale(a, mag) {
9275             return [ a[0] * mag, a[1] * mag ];
9276         }
9277
9278         // vector rounding (was: geoRoundCoordinates)
9279         function geoVecFloor(a) {
9280             return [ Math.floor(a[0]), Math.floor(a[1]) ];
9281         }
9282
9283         // linear interpolation
9284         function geoVecInterp(a, b, t) {
9285             return [
9286                 a[0] + (b[0] - a[0]) * t,
9287                 a[1] + (b[1] - a[1]) * t
9288             ];
9289         }
9290
9291         // http://jsperf.com/id-dist-optimization
9292         function geoVecLength(a, b) {
9293             return Math.sqrt(geoVecLengthSquare(a,b));
9294         }
9295
9296         // length of vector raised to the power two
9297         function geoVecLengthSquare(a, b) {
9298             b = b || [0, 0];
9299             var x = a[0] - b[0];
9300             var y = a[1] - b[1];
9301             return (x * x) + (y * y);
9302         }
9303
9304         // get a unit vector
9305         function geoVecNormalize(a) {
9306             var length = Math.sqrt((a[0] * a[0]) + (a[1] * a[1]));
9307             if (length !== 0) {
9308                 return geoVecScale(a, 1 / length);
9309             }
9310             return [0, 0];
9311         }
9312
9313         // Return the counterclockwise angle in the range (-pi, pi)
9314         // between the positive X axis and the line intersecting a and b.
9315         function geoVecAngle(a, b) {
9316             return Math.atan2(b[1] - a[1], b[0] - a[0]);
9317         }
9318
9319         // dot product
9320         function geoVecDot(a, b, origin) {
9321             origin = origin || [0, 0];
9322             var p = geoVecSubtract(a, origin);
9323             var q = geoVecSubtract(b, origin);
9324             return (p[0]) * (q[0]) + (p[1]) * (q[1]);
9325         }
9326
9327         // normalized dot product
9328         function geoVecNormalizedDot(a, b, origin) {
9329             origin = origin || [0, 0];
9330             var p = geoVecNormalize(geoVecSubtract(a, origin));
9331             var q = geoVecNormalize(geoVecSubtract(b, origin));
9332             return geoVecDot(p, q);
9333         }
9334
9335         // 2D cross product of OA and OB vectors, returns magnitude of Z vector
9336         // Returns a positive value, if OAB makes a counter-clockwise turn,
9337         // negative for clockwise turn, and zero if the points are collinear.
9338         function geoVecCross(a, b, origin) {
9339             origin = origin || [0, 0];
9340             var p = geoVecSubtract(a, origin);
9341             var q = geoVecSubtract(b, origin);
9342             return (p[0]) * (q[1]) - (p[1]) * (q[0]);
9343         }
9344
9345
9346         // find closest orthogonal projection of point onto points array
9347         function geoVecProject(a, points) {
9348             var min = Infinity;
9349             var idx;
9350             var target;
9351
9352             for (var i = 0; i < points.length - 1; i++) {
9353                 var o = points[i];
9354                 var s = geoVecSubtract(points[i + 1], o);
9355                 var v = geoVecSubtract(a, o);
9356                 var proj = geoVecDot(v, s) / geoVecDot(s, s);
9357                 var p;
9358
9359                 if (proj < 0) {
9360                     p = o;
9361                 } else if (proj > 1) {
9362                     p = points[i + 1];
9363                 } else {
9364                     p = [o[0] + proj * s[0], o[1] + proj * s[1]];
9365                 }
9366
9367                 var dist = geoVecLength(p, a);
9368                 if (dist < min) {
9369                     min = dist;
9370                     idx = i + 1;
9371                     target = p;
9372                 }
9373             }
9374
9375             if (idx !== undefined) {
9376                 return { index: idx, distance: min, target: target };
9377             } else {
9378                 return null;
9379             }
9380         }
9381
9382         // Return the counterclockwise angle in the range (-pi, pi)
9383         // between the positive X axis and the line intersecting a and b.
9384         function geoAngle(a, b, projection) {
9385             return geoVecAngle(projection(a.loc), projection(b.loc));
9386         }
9387
9388
9389         function geoEdgeEqual(a, b) {
9390             return (a[0] === b[0] && a[1] === b[1]) ||
9391                 (a[0] === b[1] && a[1] === b[0]);
9392         }
9393
9394
9395         // Rotate all points counterclockwise around a pivot point by given angle
9396         function geoRotate(points, angle, around) {
9397             return points.map(function(point) {
9398                 var radial = geoVecSubtract(point, around);
9399                 return [
9400                     radial[0] * Math.cos(angle) - radial[1] * Math.sin(angle) + around[0],
9401                     radial[0] * Math.sin(angle) + radial[1] * Math.cos(angle) + around[1]
9402                 ];
9403             });
9404         }
9405
9406
9407         // Choose the edge with the minimal distance from `point` to its orthogonal
9408         // projection onto that edge, if such a projection exists, or the distance to
9409         // the closest vertex on that edge. Returns an object with the `index` of the
9410         // chosen edge, the chosen `loc` on that edge, and the `distance` to to it.
9411         function geoChooseEdge(nodes, point, projection, activeID) {
9412             var dist = geoVecLength;
9413             var points = nodes.map(function(n) { return projection(n.loc); });
9414             var ids = nodes.map(function(n) { return n.id; });
9415             var min = Infinity;
9416             var idx;
9417             var loc;
9418
9419             for (var i = 0; i < points.length - 1; i++) {
9420                 if (ids[i] === activeID || ids[i + 1] === activeID) { continue; }
9421
9422                 var o = points[i];
9423                 var s = geoVecSubtract(points[i + 1], o);
9424                 var v = geoVecSubtract(point, o);
9425                 var proj = geoVecDot(v, s) / geoVecDot(s, s);
9426                 var p;
9427
9428                 if (proj < 0) {
9429                     p = o;
9430                 } else if (proj > 1) {
9431                     p = points[i + 1];
9432                 } else {
9433                     p = [o[0] + proj * s[0], o[1] + proj * s[1]];
9434                 }
9435
9436                 var d = dist(p, point);
9437                 if (d < min) {
9438                     min = d;
9439                     idx = i + 1;
9440                     loc = projection.invert(p);
9441                 }
9442             }
9443
9444             if (idx !== undefined) {
9445                 return { index: idx, distance: min, loc: loc };
9446             } else {
9447                 return null;
9448             }
9449         }
9450
9451
9452         // Test active (dragged or drawing) segments against inactive segments
9453         // This is used to test e.g. multipolygon rings that cross
9454         // `activeNodes` is the ring containing the activeID being dragged.
9455         // `inactiveNodes` is the other ring to test against
9456         function geoHasLineIntersections(activeNodes, inactiveNodes, activeID) {
9457             var actives = [];
9458             var inactives = [];
9459             var j, k, n1, n2, segment;
9460
9461             // gather active segments (only segments in activeNodes that contain the activeID)
9462             for (j = 0; j < activeNodes.length - 1; j++) {
9463                 n1 = activeNodes[j];
9464                 n2 = activeNodes[j+1];
9465                 segment = [n1.loc, n2.loc];
9466                 if (n1.id === activeID || n2.id === activeID) {
9467                     actives.push(segment);
9468                 }
9469             }
9470
9471             // gather inactive segments
9472             for (j = 0; j < inactiveNodes.length - 1; j++) {
9473                 n1 = inactiveNodes[j];
9474                 n2 = inactiveNodes[j+1];
9475                 segment = [n1.loc, n2.loc];
9476                 inactives.push(segment);
9477             }
9478
9479             // test
9480             for (j = 0; j < actives.length; j++) {
9481                 for (k = 0; k < inactives.length; k++) {
9482                     var p = actives[j];
9483                     var q = inactives[k];
9484                     var hit = geoLineIntersection(p, q);
9485                     if (hit) {
9486                         return true;
9487                     }
9488                 }
9489             }
9490
9491             return false;
9492         }
9493
9494
9495         // Test active (dragged or drawing) segments against inactive segments
9496         // This is used to test whether a way intersects with itself.
9497         function geoHasSelfIntersections(nodes, activeID) {
9498             var actives = [];
9499             var inactives = [];
9500             var j, k;
9501
9502             // group active and passive segments along the nodes
9503             for (j = 0; j < nodes.length - 1; j++) {
9504                 var n1 = nodes[j];
9505                 var n2 = nodes[j+1];
9506                 var segment = [n1.loc, n2.loc];
9507                 if (n1.id === activeID || n2.id === activeID) {
9508                     actives.push(segment);
9509                 } else {
9510                     inactives.push(segment);
9511                 }
9512             }
9513
9514             // test
9515             for (j = 0; j < actives.length; j++) {
9516                 for (k = 0; k < inactives.length; k++) {
9517                     var p = actives[j];
9518                     var q = inactives[k];
9519                     // skip if segments share an endpoint
9520                     if (geoVecEqual(p[1], q[0]) || geoVecEqual(p[0], q[1]) ||
9521                         geoVecEqual(p[0], q[0]) || geoVecEqual(p[1], q[1]) ) {
9522                         continue;
9523                     }
9524
9525                     var hit = geoLineIntersection(p, q);
9526                     if (hit) {
9527                         var epsilon = 1e-8;
9528                         // skip if the hit is at the segment's endpoint
9529                         if (geoVecEqual(p[1], hit, epsilon) || geoVecEqual(p[0], hit, epsilon) ||
9530                             geoVecEqual(q[1], hit, epsilon) || geoVecEqual(q[0], hit, epsilon) ) {
9531                             continue;
9532                         } else {
9533                             return true;
9534                         }
9535                     }
9536                 }
9537             }
9538
9539             return false;
9540         }
9541
9542
9543         // Return the intersection point of 2 line segments.
9544         // From https://github.com/pgkelley4/line-segments-intersect
9545         // This uses the vector cross product approach described below:
9546         //  http://stackoverflow.com/a/565282/786339
9547         function geoLineIntersection(a, b) {
9548             var p = [a[0][0], a[0][1]];
9549             var p2 = [a[1][0], a[1][1]];
9550             var q = [b[0][0], b[0][1]];
9551             var q2 = [b[1][0], b[1][1]];
9552             var r = geoVecSubtract(p2, p);
9553             var s = geoVecSubtract(q2, q);
9554             var uNumerator = geoVecCross(geoVecSubtract(q, p), r);
9555             var denominator = geoVecCross(r, s);
9556
9557             if (uNumerator && denominator) {
9558                 var u = uNumerator / denominator;
9559                 var t = geoVecCross(geoVecSubtract(q, p), s) / denominator;
9560
9561                 if ((t >= 0) && (t <= 1) && (u >= 0) && (u <= 1)) {
9562                     return geoVecInterp(p, p2, t);
9563                 }
9564             }
9565
9566             return null;
9567         }
9568
9569
9570         function geoPathIntersections(path1, path2) {
9571             var intersections = [];
9572             for (var i = 0; i < path1.length - 1; i++) {
9573                 for (var j = 0; j < path2.length - 1; j++) {
9574                     var a = [ path1[i], path1[i+1] ];
9575                     var b = [ path2[j], path2[j+1] ];
9576                     var hit = geoLineIntersection(a, b);
9577                     if (hit) {
9578                         intersections.push(hit);
9579                     }
9580                 }
9581             }
9582             return intersections;
9583         }
9584
9585         function geoPathHasIntersections(path1, path2) {
9586             for (var i = 0; i < path1.length - 1; i++) {
9587                 for (var j = 0; j < path2.length - 1; j++) {
9588                     var a = [ path1[i], path1[i+1] ];
9589                     var b = [ path2[j], path2[j+1] ];
9590                     var hit = geoLineIntersection(a, b);
9591                     if (hit) {
9592                         return true;
9593                     }
9594                 }
9595             }
9596             return false;
9597         }
9598
9599
9600         // Return whether point is contained in polygon.
9601         //
9602         // `point` should be a 2-item array of coordinates.
9603         // `polygon` should be an array of 2-item arrays of coordinates.
9604         //
9605         // From https://github.com/substack/point-in-polygon.
9606         // ray-casting algorithm based on
9607         // http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html
9608         //
9609         function geoPointInPolygon(point, polygon) {
9610             var x = point[0];
9611             var y = point[1];
9612             var inside = false;
9613
9614             for (var i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
9615                 var xi = polygon[i][0];
9616                 var yi = polygon[i][1];
9617                 var xj = polygon[j][0];
9618                 var yj = polygon[j][1];
9619
9620                 var intersect = ((yi > y) !== (yj > y)) &&
9621                     (x < (xj - xi) * (y - yi) / (yj - yi) + xi);
9622                 if (intersect) { inside = !inside; }
9623             }
9624
9625             return inside;
9626         }
9627
9628
9629         function geoPolygonContainsPolygon(outer, inner) {
9630             return inner.every(function(point) {
9631                 return geoPointInPolygon(point, outer);
9632             });
9633         }
9634
9635
9636         function geoPolygonIntersectsPolygon(outer, inner, checkSegments) {
9637             function testPoints(outer, inner) {
9638                 return inner.some(function(point) {
9639                     return geoPointInPolygon(point, outer);
9640                 });
9641             }
9642
9643            return testPoints(outer, inner) || (!!checkSegments && geoPathHasIntersections(outer, inner));
9644         }
9645
9646
9647         // http://gis.stackexchange.com/questions/22895/finding-minimum-area-rectangle-for-given-points
9648         // http://gis.stackexchange.com/questions/3739/generalisation-strategies-for-building-outlines/3756#3756
9649         function geoGetSmallestSurroundingRectangle(points) {
9650             var hull = d3_polygonHull(points);
9651             var centroid = d3_polygonCentroid(hull);
9652             var minArea = Infinity;
9653             var ssrExtent = [];
9654             var ssrAngle = 0;
9655             var c1 = hull[0];
9656
9657             for (var i = 0; i <= hull.length - 1; i++) {
9658                 var c2 = (i === hull.length - 1) ? hull[0] : hull[i + 1];
9659                 var angle = Math.atan2(c2[1] - c1[1], c2[0] - c1[0]);
9660                 var poly = geoRotate(hull, -angle, centroid);
9661                 var extent = poly.reduce(function(extent, point) {
9662                     return extent.extend(geoExtent(point));
9663                 }, geoExtent());
9664
9665                 var area = extent.area();
9666                 if (area < minArea) {
9667                     minArea = area;
9668                     ssrExtent = extent;
9669                     ssrAngle = angle;
9670                 }
9671                 c1 = c2;
9672             }
9673
9674             return {
9675                 poly: geoRotate(ssrExtent.polygon(), ssrAngle, centroid),
9676                 angle: ssrAngle
9677             };
9678         }
9679
9680
9681         function geoPathLength(path) {
9682             var length = 0;
9683             for (var i = 0; i < path.length - 1; i++) {
9684                 length += geoVecLength(path[i], path[i + 1]);
9685             }
9686             return length;
9687         }
9688
9689
9690         // If the given point is at the edge of the padded viewport,
9691         // return a vector that will nudge the viewport in that direction
9692         function geoViewportEdge(point, dimensions) {
9693             var pad = [80, 20, 50, 20];   // top, right, bottom, left
9694             var x = 0;
9695             var y = 0;
9696
9697             if (point[0] > dimensions[0] - pad[1])
9698                 { x = -10; }
9699             if (point[0] < pad[3])
9700                 { x = 10; }
9701             if (point[1] > dimensions[1] - pad[2])
9702                 { y = -10; }
9703             if (point[1] < pad[0])
9704                 { y = 10; }
9705
9706             if (x || y) {
9707                 return [x, y];
9708             } else {
9709                 return null;
9710             }
9711         }
9712
9713         var noop$3 = {value: function() {}};
9714
9715         function dispatch() {
9716           var arguments$1 = arguments;
9717
9718           for (var i = 0, n = arguments.length, _ = {}, t; i < n; ++i) {
9719             if (!(t = arguments$1[i] + "") || (t in _) || /[\s.]/.test(t)) { throw new Error("illegal type: " + t); }
9720             _[t] = [];
9721           }
9722           return new Dispatch(_);
9723         }
9724
9725         function Dispatch(_) {
9726           this._ = _;
9727         }
9728
9729         function parseTypenames(typenames, types) {
9730           return typenames.trim().split(/^|\s+/).map(function(t) {
9731             var name = "", i = t.indexOf(".");
9732             if (i >= 0) { name = t.slice(i + 1), t = t.slice(0, i); }
9733             if (t && !types.hasOwnProperty(t)) { throw new Error("unknown type: " + t); }
9734             return {type: t, name: name};
9735           });
9736         }
9737
9738         Dispatch.prototype = dispatch.prototype = {
9739           constructor: Dispatch,
9740           on: function(typename, callback) {
9741             var _ = this._,
9742                 T = parseTypenames(typename + "", _),
9743                 t,
9744                 i = -1,
9745                 n = T.length;
9746
9747             // If no callback was specified, return the callback of the given type and name.
9748             if (arguments.length < 2) {
9749               while (++i < n) { if ((t = (typename = T[i]).type) && (t = get$1(_[t], typename.name))) { return t; } }
9750               return;
9751             }
9752
9753             // If a type was specified, set the callback for the given type and name.
9754             // Otherwise, if a null callback was specified, remove callbacks of the given name.
9755             if (callback != null && typeof callback !== "function") { throw new Error("invalid callback: " + callback); }
9756             while (++i < n) {
9757               if (t = (typename = T[i]).type) { _[t] = set(_[t], typename.name, callback); }
9758               else if (callback == null) { for (t in _) { _[t] = set(_[t], typename.name, null); } }
9759             }
9760
9761             return this;
9762           },
9763           copy: function() {
9764             var copy = {}, _ = this._;
9765             for (var t in _) { copy[t] = _[t].slice(); }
9766             return new Dispatch(copy);
9767           },
9768           call: function(type, that) {
9769             var arguments$1 = arguments;
9770
9771             if ((n = arguments.length - 2) > 0) { for (var args = new Array(n), i = 0, n, t; i < n; ++i) { args[i] = arguments$1[i + 2]; } }
9772             if (!this._.hasOwnProperty(type)) { throw new Error("unknown type: " + type); }
9773             for (t = this._[type], i = 0, n = t.length; i < n; ++i) { t[i].value.apply(that, args); }
9774           },
9775           apply: function(type, that, args) {
9776             if (!this._.hasOwnProperty(type)) { throw new Error("unknown type: " + type); }
9777             for (var t = this._[type], i = 0, n = t.length; i < n; ++i) { t[i].value.apply(that, args); }
9778           }
9779         };
9780
9781         function get$1(type, name) {
9782           for (var i = 0, n = type.length, c; i < n; ++i) {
9783             if ((c = type[i]).name === name) {
9784               return c.value;
9785             }
9786           }
9787         }
9788
9789         function set(type, name, callback) {
9790           for (var i = 0, n = type.length; i < n; ++i) {
9791             if (type[i].name === name) {
9792               type[i] = noop$3, type = type.slice(0, i).concat(type.slice(i + 1));
9793               break;
9794             }
9795           }
9796           if (callback != null) { type.push({name: name, value: callback}); }
9797           return type;
9798         }
9799
9800         var xhtml = "http://www.w3.org/1999/xhtml";
9801
9802         var namespaces = {
9803           svg: "http://www.w3.org/2000/svg",
9804           xhtml: xhtml,
9805           xlink: "http://www.w3.org/1999/xlink",
9806           xml: "http://www.w3.org/XML/1998/namespace",
9807           xmlns: "http://www.w3.org/2000/xmlns/"
9808         };
9809
9810         function namespace(name) {
9811           var prefix = name += "", i = prefix.indexOf(":");
9812           if (i >= 0 && (prefix = name.slice(0, i)) !== "xmlns") { name = name.slice(i + 1); }
9813           return namespaces.hasOwnProperty(prefix) ? {space: namespaces[prefix], local: name} : name;
9814         }
9815
9816         function creatorInherit(name) {
9817           return function() {
9818             var document = this.ownerDocument,
9819                 uri = this.namespaceURI;
9820             return uri === xhtml && document.documentElement.namespaceURI === xhtml
9821                 ? document.createElement(name)
9822                 : document.createElementNS(uri, name);
9823           };
9824         }
9825
9826         function creatorFixed(fullname) {
9827           return function() {
9828             return this.ownerDocument.createElementNS(fullname.space, fullname.local);
9829           };
9830         }
9831
9832         function creator(name) {
9833           var fullname = namespace(name);
9834           return (fullname.local
9835               ? creatorFixed
9836               : creatorInherit)(fullname);
9837         }
9838
9839         function none() {}
9840
9841         function selector(selector) {
9842           return selector == null ? none : function() {
9843             return this.querySelector(selector);
9844           };
9845         }
9846
9847         function selection_select(select) {
9848           if (typeof select !== "function") { select = selector(select); }
9849
9850           for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) {
9851             for (var group = groups[j], n = group.length, subgroup = subgroups[j] = new Array(n), node, subnode, i = 0; i < n; ++i) {
9852               if ((node = group[i]) && (subnode = select.call(node, node.__data__, i, group))) {
9853                 if ("__data__" in node) { subnode.__data__ = node.__data__; }
9854                 subgroup[i] = subnode;
9855               }
9856             }
9857           }
9858
9859           return new Selection(subgroups, this._parents);
9860         }
9861
9862         function empty() {
9863           return [];
9864         }
9865
9866         function selectorAll(selector) {
9867           return selector == null ? empty : function() {
9868             return this.querySelectorAll(selector);
9869           };
9870         }
9871
9872         function selection_selectAll(select) {
9873           if (typeof select !== "function") { select = selectorAll(select); }
9874
9875           for (var groups = this._groups, m = groups.length, subgroups = [], parents = [], j = 0; j < m; ++j) {
9876             for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) {
9877               if (node = group[i]) {
9878                 subgroups.push(select.call(node, node.__data__, i, group));
9879                 parents.push(node);
9880               }
9881             }
9882           }
9883
9884           return new Selection(subgroups, parents);
9885         }
9886
9887         function matcher(selector) {
9888           return function() {
9889             return this.matches(selector);
9890           };
9891         }
9892
9893         function selection_filter(match) {
9894           if (typeof match !== "function") { match = matcher(match); }
9895
9896           for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) {
9897             for (var group = groups[j], n = group.length, subgroup = subgroups[j] = [], node, i = 0; i < n; ++i) {
9898               if ((node = group[i]) && match.call(node, node.__data__, i, group)) {
9899                 subgroup.push(node);
9900               }
9901             }
9902           }
9903
9904           return new Selection(subgroups, this._parents);
9905         }
9906
9907         function sparse(update) {
9908           return new Array(update.length);
9909         }
9910
9911         function selection_enter() {
9912           return new Selection(this._enter || this._groups.map(sparse), this._parents);
9913         }
9914
9915         function EnterNode(parent, datum) {
9916           this.ownerDocument = parent.ownerDocument;
9917           this.namespaceURI = parent.namespaceURI;
9918           this._next = null;
9919           this._parent = parent;
9920           this.__data__ = datum;
9921         }
9922
9923         EnterNode.prototype = {
9924           constructor: EnterNode,
9925           appendChild: function(child) { return this._parent.insertBefore(child, this._next); },
9926           insertBefore: function(child, next) { return this._parent.insertBefore(child, next); },
9927           querySelector: function(selector) { return this._parent.querySelector(selector); },
9928           querySelectorAll: function(selector) { return this._parent.querySelectorAll(selector); }
9929         };
9930
9931         function constant(x) {
9932           return function() {
9933             return x;
9934           };
9935         }
9936
9937         var keyPrefix = "$"; // Protect against keys like “__proto__”.
9938
9939         function bindIndex(parent, group, enter, update, exit, data) {
9940           var i = 0,
9941               node,
9942               groupLength = group.length,
9943               dataLength = data.length;
9944
9945           // Put any non-null nodes that fit into update.
9946           // Put any null nodes into enter.
9947           // Put any remaining data into enter.
9948           for (; i < dataLength; ++i) {
9949             if (node = group[i]) {
9950               node.__data__ = data[i];
9951               update[i] = node;
9952             } else {
9953               enter[i] = new EnterNode(parent, data[i]);
9954             }
9955           }
9956
9957           // Put any non-null nodes that don’t fit into exit.
9958           for (; i < groupLength; ++i) {
9959             if (node = group[i]) {
9960               exit[i] = node;
9961             }
9962           }
9963         }
9964
9965         function bindKey(parent, group, enter, update, exit, data, key) {
9966           var i,
9967               node,
9968               nodeByKeyValue = {},
9969               groupLength = group.length,
9970               dataLength = data.length,
9971               keyValues = new Array(groupLength),
9972               keyValue;
9973
9974           // Compute the key for each node.
9975           // If multiple nodes have the same key, the duplicates are added to exit.
9976           for (i = 0; i < groupLength; ++i) {
9977             if (node = group[i]) {
9978               keyValues[i] = keyValue = keyPrefix + key.call(node, node.__data__, i, group);
9979               if (keyValue in nodeByKeyValue) {
9980                 exit[i] = node;
9981               } else {
9982                 nodeByKeyValue[keyValue] = node;
9983               }
9984             }
9985           }
9986
9987           // Compute the key for each datum.
9988           // If there a node associated with this key, join and add it to update.
9989           // If there is not (or the key is a duplicate), add it to enter.
9990           for (i = 0; i < dataLength; ++i) {
9991             keyValue = keyPrefix + key.call(parent, data[i], i, data);
9992             if (node = nodeByKeyValue[keyValue]) {
9993               update[i] = node;
9994               node.__data__ = data[i];
9995               nodeByKeyValue[keyValue] = null;
9996             } else {
9997               enter[i] = new EnterNode(parent, data[i]);
9998             }
9999           }
10000
10001           // Add any remaining nodes that were not bound to data to exit.
10002           for (i = 0; i < groupLength; ++i) {
10003             if ((node = group[i]) && (nodeByKeyValue[keyValues[i]] === node)) {
10004               exit[i] = node;
10005             }
10006           }
10007         }
10008
10009         function selection_data(value, key) {
10010           if (!value) {
10011             data = new Array(this.size()), j = -1;
10012             this.each(function(d) { data[++j] = d; });
10013             return data;
10014           }
10015
10016           var bind = key ? bindKey : bindIndex,
10017               parents = this._parents,
10018               groups = this._groups;
10019
10020           if (typeof value !== "function") { value = constant(value); }
10021
10022           for (var m = groups.length, update = new Array(m), enter = new Array(m), exit = new Array(m), j = 0; j < m; ++j) {
10023             var parent = parents[j],
10024                 group = groups[j],
10025                 groupLength = group.length,
10026                 data = value.call(parent, parent && parent.__data__, j, parents),
10027                 dataLength = data.length,
10028                 enterGroup = enter[j] = new Array(dataLength),
10029                 updateGroup = update[j] = new Array(dataLength),
10030                 exitGroup = exit[j] = new Array(groupLength);
10031
10032             bind(parent, group, enterGroup, updateGroup, exitGroup, data, key);
10033
10034             // Now connect the enter nodes to their following update node, such that
10035             // appendChild can insert the materialized enter node before this node,
10036             // rather than at the end of the parent node.
10037             for (var i0 = 0, i1 = 0, previous, next; i0 < dataLength; ++i0) {
10038               if (previous = enterGroup[i0]) {
10039                 if (i0 >= i1) { i1 = i0 + 1; }
10040                 while (!(next = updateGroup[i1]) && ++i1 < dataLength){ }
10041                 previous._next = next || null;
10042               }
10043             }
10044           }
10045
10046           update = new Selection(update, parents);
10047           update._enter = enter;
10048           update._exit = exit;
10049           return update;
10050         }
10051
10052         function selection_exit() {
10053           return new Selection(this._exit || this._groups.map(sparse), this._parents);
10054         }
10055
10056         function selection_join(onenter, onupdate, onexit) {
10057           var enter = this.enter(), update = this, exit = this.exit();
10058           enter = typeof onenter === "function" ? onenter(enter) : enter.append(onenter + "");
10059           if (onupdate != null) { update = onupdate(update); }
10060           if (onexit == null) { exit.remove(); } else { onexit(exit); }
10061           return enter && update ? enter.merge(update).order() : update;
10062         }
10063
10064         function selection_merge(selection) {
10065
10066           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) {
10067             for (var group0 = groups0[j], group1 = groups1[j], n = group0.length, merge = merges[j] = new Array(n), node, i = 0; i < n; ++i) {
10068               if (node = group0[i] || group1[i]) {
10069                 merge[i] = node;
10070               }
10071             }
10072           }
10073
10074           for (; j < m0; ++j) {
10075             merges[j] = groups0[j];
10076           }
10077
10078           return new Selection(merges, this._parents);
10079         }
10080
10081         function selection_order() {
10082
10083           for (var groups = this._groups, j = -1, m = groups.length; ++j < m;) {
10084             for (var group = groups[j], i = group.length - 1, next = group[i], node; --i >= 0;) {
10085               if (node = group[i]) {
10086                 if (next && node.compareDocumentPosition(next) ^ 4) { next.parentNode.insertBefore(node, next); }
10087                 next = node;
10088               }
10089             }
10090           }
10091
10092           return this;
10093         }
10094
10095         function selection_sort(compare) {
10096           if (!compare) { compare = ascending; }
10097
10098           function compareNode(a, b) {
10099             return a && b ? compare(a.__data__, b.__data__) : !a - !b;
10100           }
10101
10102           for (var groups = this._groups, m = groups.length, sortgroups = new Array(m), j = 0; j < m; ++j) {
10103             for (var group = groups[j], n = group.length, sortgroup = sortgroups[j] = new Array(n), node, i = 0; i < n; ++i) {
10104               if (node = group[i]) {
10105                 sortgroup[i] = node;
10106               }
10107             }
10108             sortgroup.sort(compareNode);
10109           }
10110
10111           return new Selection(sortgroups, this._parents).order();
10112         }
10113
10114         function ascending(a, b) {
10115           return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN;
10116         }
10117
10118         function selection_call() {
10119           var callback = arguments[0];
10120           arguments[0] = this;
10121           callback.apply(null, arguments);
10122           return this;
10123         }
10124
10125         function selection_nodes() {
10126           var nodes = new Array(this.size()), i = -1;
10127           this.each(function() { nodes[++i] = this; });
10128           return nodes;
10129         }
10130
10131         function selection_node() {
10132
10133           for (var groups = this._groups, j = 0, m = groups.length; j < m; ++j) {
10134             for (var group = groups[j], i = 0, n = group.length; i < n; ++i) {
10135               var node = group[i];
10136               if (node) { return node; }
10137             }
10138           }
10139
10140           return null;
10141         }
10142
10143         function selection_size() {
10144           var size = 0;
10145           this.each(function() { ++size; });
10146           return size;
10147         }
10148
10149         function selection_empty() {
10150           return !this.node();
10151         }
10152
10153         function selection_each(callback) {
10154
10155           for (var groups = this._groups, j = 0, m = groups.length; j < m; ++j) {
10156             for (var group = groups[j], i = 0, n = group.length, node; i < n; ++i) {
10157               if (node = group[i]) { callback.call(node, node.__data__, i, group); }
10158             }
10159           }
10160
10161           return this;
10162         }
10163
10164         function attrRemove(name) {
10165           return function() {
10166             this.removeAttribute(name);
10167           };
10168         }
10169
10170         function attrRemoveNS(fullname) {
10171           return function() {
10172             this.removeAttributeNS(fullname.space, fullname.local);
10173           };
10174         }
10175
10176         function attrConstant(name, value) {
10177           return function() {
10178             this.setAttribute(name, value);
10179           };
10180         }
10181
10182         function attrConstantNS(fullname, value) {
10183           return function() {
10184             this.setAttributeNS(fullname.space, fullname.local, value);
10185           };
10186         }
10187
10188         function attrFunction(name, value) {
10189           return function() {
10190             var v = value.apply(this, arguments);
10191             if (v == null) { this.removeAttribute(name); }
10192             else { this.setAttribute(name, v); }
10193           };
10194         }
10195
10196         function attrFunctionNS(fullname, value) {
10197           return function() {
10198             var v = value.apply(this, arguments);
10199             if (v == null) { this.removeAttributeNS(fullname.space, fullname.local); }
10200             else { this.setAttributeNS(fullname.space, fullname.local, v); }
10201           };
10202         }
10203
10204         function selection_attr(name, value) {
10205           var fullname = namespace(name);
10206
10207           if (arguments.length < 2) {
10208             var node = this.node();
10209             return fullname.local
10210                 ? node.getAttributeNS(fullname.space, fullname.local)
10211                 : node.getAttribute(fullname);
10212           }
10213
10214           return this.each((value == null
10215               ? (fullname.local ? attrRemoveNS : attrRemove) : (typeof value === "function"
10216               ? (fullname.local ? attrFunctionNS : attrFunction)
10217               : (fullname.local ? attrConstantNS : attrConstant)))(fullname, value));
10218         }
10219
10220         function defaultView(node) {
10221           return (node.ownerDocument && node.ownerDocument.defaultView) // node is a Node
10222               || (node.document && node) // node is a Window
10223               || node.defaultView; // node is a Document
10224         }
10225
10226         function styleRemove(name) {
10227           return function() {
10228             this.style.removeProperty(name);
10229           };
10230         }
10231
10232         function styleConstant(name, value, priority) {
10233           return function() {
10234             this.style.setProperty(name, value, priority);
10235           };
10236         }
10237
10238         function styleFunction(name, value, priority) {
10239           return function() {
10240             var v = value.apply(this, arguments);
10241             if (v == null) { this.style.removeProperty(name); }
10242             else { this.style.setProperty(name, v, priority); }
10243           };
10244         }
10245
10246         function selection_style(name, value, priority) {
10247           return arguments.length > 1
10248               ? this.each((value == null
10249                     ? styleRemove : typeof value === "function"
10250                     ? styleFunction
10251                     : styleConstant)(name, value, priority == null ? "" : priority))
10252               : styleValue(this.node(), name);
10253         }
10254
10255         function styleValue(node, name) {
10256           return node.style.getPropertyValue(name)
10257               || defaultView(node).getComputedStyle(node, null).getPropertyValue(name);
10258         }
10259
10260         function propertyRemove(name) {
10261           return function() {
10262             delete this[name];
10263           };
10264         }
10265
10266         function propertyConstant(name, value) {
10267           return function() {
10268             this[name] = value;
10269           };
10270         }
10271
10272         function propertyFunction(name, value) {
10273           return function() {
10274             var v = value.apply(this, arguments);
10275             if (v == null) { delete this[name]; }
10276             else { this[name] = v; }
10277           };
10278         }
10279
10280         function selection_property(name, value) {
10281           return arguments.length > 1
10282               ? this.each((value == null
10283                   ? propertyRemove : typeof value === "function"
10284                   ? propertyFunction
10285                   : propertyConstant)(name, value))
10286               : this.node()[name];
10287         }
10288
10289         function classArray(string) {
10290           return string.trim().split(/^|\s+/);
10291         }
10292
10293         function classList(node) {
10294           return node.classList || new ClassList(node);
10295         }
10296
10297         function ClassList(node) {
10298           this._node = node;
10299           this._names = classArray(node.getAttribute("class") || "");
10300         }
10301
10302         ClassList.prototype = {
10303           add: function(name) {
10304             var i = this._names.indexOf(name);
10305             if (i < 0) {
10306               this._names.push(name);
10307               this._node.setAttribute("class", this._names.join(" "));
10308             }
10309           },
10310           remove: function(name) {
10311             var i = this._names.indexOf(name);
10312             if (i >= 0) {
10313               this._names.splice(i, 1);
10314               this._node.setAttribute("class", this._names.join(" "));
10315             }
10316           },
10317           contains: function(name) {
10318             return this._names.indexOf(name) >= 0;
10319           }
10320         };
10321
10322         function classedAdd(node, names) {
10323           var list = classList(node), i = -1, n = names.length;
10324           while (++i < n) { list.add(names[i]); }
10325         }
10326
10327         function classedRemove(node, names) {
10328           var list = classList(node), i = -1, n = names.length;
10329           while (++i < n) { list.remove(names[i]); }
10330         }
10331
10332         function classedTrue(names) {
10333           return function() {
10334             classedAdd(this, names);
10335           };
10336         }
10337
10338         function classedFalse(names) {
10339           return function() {
10340             classedRemove(this, names);
10341           };
10342         }
10343
10344         function classedFunction(names, value) {
10345           return function() {
10346             (value.apply(this, arguments) ? classedAdd : classedRemove)(this, names);
10347           };
10348         }
10349
10350         function selection_classed(name, value) {
10351           var names = classArray(name + "");
10352
10353           if (arguments.length < 2) {
10354             var list = classList(this.node()), i = -1, n = names.length;
10355             while (++i < n) { if (!list.contains(names[i])) { return false; } }
10356             return true;
10357           }
10358
10359           return this.each((typeof value === "function"
10360               ? classedFunction : value
10361               ? classedTrue
10362               : classedFalse)(names, value));
10363         }
10364
10365         function textRemove() {
10366           this.textContent = "";
10367         }
10368
10369         function textConstant(value) {
10370           return function() {
10371             this.textContent = value;
10372           };
10373         }
10374
10375         function textFunction(value) {
10376           return function() {
10377             var v = value.apply(this, arguments);
10378             this.textContent = v == null ? "" : v;
10379           };
10380         }
10381
10382         function selection_text(value) {
10383           return arguments.length
10384               ? this.each(value == null
10385                   ? textRemove : (typeof value === "function"
10386                   ? textFunction
10387                   : textConstant)(value))
10388               : this.node().textContent;
10389         }
10390
10391         function htmlRemove() {
10392           this.innerHTML = "";
10393         }
10394
10395         function htmlConstant(value) {
10396           return function() {
10397             this.innerHTML = value;
10398           };
10399         }
10400
10401         function htmlFunction(value) {
10402           return function() {
10403             var v = value.apply(this, arguments);
10404             this.innerHTML = v == null ? "" : v;
10405           };
10406         }
10407
10408         function selection_html(value) {
10409           return arguments.length
10410               ? this.each(value == null
10411                   ? htmlRemove : (typeof value === "function"
10412                   ? htmlFunction
10413                   : htmlConstant)(value))
10414               : this.node().innerHTML;
10415         }
10416
10417         function raise() {
10418           if (this.nextSibling) { this.parentNode.appendChild(this); }
10419         }
10420
10421         function selection_raise() {
10422           return this.each(raise);
10423         }
10424
10425         function lower() {
10426           if (this.previousSibling) { this.parentNode.insertBefore(this, this.parentNode.firstChild); }
10427         }
10428
10429         function selection_lower() {
10430           return this.each(lower);
10431         }
10432
10433         function selection_append(name) {
10434           var create = typeof name === "function" ? name : creator(name);
10435           return this.select(function() {
10436             return this.appendChild(create.apply(this, arguments));
10437           });
10438         }
10439
10440         function constantNull() {
10441           return null;
10442         }
10443
10444         function selection_insert(name, before) {
10445           var create = typeof name === "function" ? name : creator(name),
10446               select = before == null ? constantNull : typeof before === "function" ? before : selector(before);
10447           return this.select(function() {
10448             return this.insertBefore(create.apply(this, arguments), select.apply(this, arguments) || null);
10449           });
10450         }
10451
10452         function remove() {
10453           var parent = this.parentNode;
10454           if (parent) { parent.removeChild(this); }
10455         }
10456
10457         function selection_remove() {
10458           return this.each(remove);
10459         }
10460
10461         function selection_cloneShallow() {
10462           var clone = this.cloneNode(false), parent = this.parentNode;
10463           return parent ? parent.insertBefore(clone, this.nextSibling) : clone;
10464         }
10465
10466         function selection_cloneDeep() {
10467           var clone = this.cloneNode(true), parent = this.parentNode;
10468           return parent ? parent.insertBefore(clone, this.nextSibling) : clone;
10469         }
10470
10471         function selection_clone(deep) {
10472           return this.select(deep ? selection_cloneDeep : selection_cloneShallow);
10473         }
10474
10475         function selection_datum(value) {
10476           return arguments.length
10477               ? this.property("__data__", value)
10478               : this.node().__data__;
10479         }
10480
10481         var filterEvents = {};
10482
10483         var event = null;
10484
10485         if (typeof document !== "undefined") {
10486           var element = document.documentElement;
10487           if (!("onmouseenter" in element)) {
10488             filterEvents = {mouseenter: "mouseover", mouseleave: "mouseout"};
10489           }
10490         }
10491
10492         function filterContextListener(listener, index, group) {
10493           listener = contextListener(listener, index, group);
10494           return function(event) {
10495             var related = event.relatedTarget;
10496             if (!related || (related !== this && !(related.compareDocumentPosition(this) & 8))) {
10497               listener.call(this, event);
10498             }
10499           };
10500         }
10501
10502         function contextListener(listener, index, group) {
10503           return function(event1) {
10504             var event0 = event; // Events can be reentrant (e.g., focus).
10505             event = event1;
10506             try {
10507               listener.call(this, this.__data__, index, group);
10508             } finally {
10509               event = event0;
10510             }
10511           };
10512         }
10513
10514         function parseTypenames$1(typenames) {
10515           return typenames.trim().split(/^|\s+/).map(function(t) {
10516             var name = "", i = t.indexOf(".");
10517             if (i >= 0) { name = t.slice(i + 1), t = t.slice(0, i); }
10518             return {type: t, name: name};
10519           });
10520         }
10521
10522         function onRemove(typename) {
10523           return function() {
10524             var on = this.__on;
10525             if (!on) { return; }
10526             for (var j = 0, i = -1, m = on.length, o; j < m; ++j) {
10527               if (o = on[j], (!typename.type || o.type === typename.type) && o.name === typename.name) {
10528                 this.removeEventListener(o.type, o.listener, o.capture);
10529               } else {
10530                 on[++i] = o;
10531               }
10532             }
10533             if (++i) { on.length = i; }
10534             else { delete this.__on; }
10535           };
10536         }
10537
10538         function onAdd(typename, value, capture) {
10539           var wrap = filterEvents.hasOwnProperty(typename.type) ? filterContextListener : contextListener;
10540           return function(d, i, group) {
10541             var on = this.__on, o, listener = wrap(value, i, group);
10542             if (on) { for (var j = 0, m = on.length; j < m; ++j) {
10543               if ((o = on[j]).type === typename.type && o.name === typename.name) {
10544                 this.removeEventListener(o.type, o.listener, o.capture);
10545                 this.addEventListener(o.type, o.listener = listener, o.capture = capture);
10546                 o.value = value;
10547                 return;
10548               }
10549             } }
10550             this.addEventListener(typename.type, listener, capture);
10551             o = {type: typename.type, name: typename.name, value: value, listener: listener, capture: capture};
10552             if (!on) { this.__on = [o]; }
10553             else { on.push(o); }
10554           };
10555         }
10556
10557         function selection_on(typename, value, capture) {
10558           var typenames = parseTypenames$1(typename + ""), i, n = typenames.length, t;
10559
10560           if (arguments.length < 2) {
10561             var on = this.node().__on;
10562             if (on) { for (var j = 0, m = on.length, o; j < m; ++j) {
10563               for (i = 0, o = on[j]; i < n; ++i) {
10564                 if ((t = typenames[i]).type === o.type && t.name === o.name) {
10565                   return o.value;
10566                 }
10567               }
10568             } }
10569             return;
10570           }
10571
10572           on = value ? onAdd : onRemove;
10573           if (capture == null) { capture = false; }
10574           for (i = 0; i < n; ++i) { this.each(on(typenames[i], value, capture)); }
10575           return this;
10576         }
10577
10578         function customEvent(event1, listener, that, args) {
10579           var event0 = event;
10580           event1.sourceEvent = event;
10581           event = event1;
10582           try {
10583             return listener.apply(that, args);
10584           } finally {
10585             event = event0;
10586           }
10587         }
10588
10589         function dispatchEvent(node, type, params) {
10590           var window = defaultView(node),
10591               event = window.CustomEvent;
10592
10593           if (typeof event === "function") {
10594             event = new event(type, params);
10595           } else {
10596             event = window.document.createEvent("Event");
10597             if (params) { event.initEvent(type, params.bubbles, params.cancelable), event.detail = params.detail; }
10598             else { event.initEvent(type, false, false); }
10599           }
10600
10601           node.dispatchEvent(event);
10602         }
10603
10604         function dispatchConstant(type, params) {
10605           return function() {
10606             return dispatchEvent(this, type, params);
10607           };
10608         }
10609
10610         function dispatchFunction(type, params) {
10611           return function() {
10612             return dispatchEvent(this, type, params.apply(this, arguments));
10613           };
10614         }
10615
10616         function selection_dispatch(type, params) {
10617           return this.each((typeof params === "function"
10618               ? dispatchFunction
10619               : dispatchConstant)(type, params));
10620         }
10621
10622         var root$1 = [null];
10623
10624         function Selection(groups, parents) {
10625           this._groups = groups;
10626           this._parents = parents;
10627         }
10628
10629         function selection() {
10630           return new Selection([[document.documentElement]], root$1);
10631         }
10632
10633         Selection.prototype = selection.prototype = {
10634           constructor: Selection,
10635           select: selection_select,
10636           selectAll: selection_selectAll,
10637           filter: selection_filter,
10638           data: selection_data,
10639           enter: selection_enter,
10640           exit: selection_exit,
10641           join: selection_join,
10642           merge: selection_merge,
10643           order: selection_order,
10644           sort: selection_sort,
10645           call: selection_call,
10646           nodes: selection_nodes,
10647           node: selection_node,
10648           size: selection_size,
10649           empty: selection_empty,
10650           each: selection_each,
10651           attr: selection_attr,
10652           style: selection_style,
10653           property: selection_property,
10654           classed: selection_classed,
10655           text: selection_text,
10656           html: selection_html,
10657           raise: selection_raise,
10658           lower: selection_lower,
10659           append: selection_append,
10660           insert: selection_insert,
10661           remove: selection_remove,
10662           clone: selection_clone,
10663           datum: selection_datum,
10664           on: selection_on,
10665           dispatch: selection_dispatch
10666         };
10667
10668         function select(selector) {
10669           return typeof selector === "string"
10670               ? new Selection([[document.querySelector(selector)]], [document.documentElement])
10671               : new Selection([[selector]], root$1);
10672         }
10673
10674         function sourceEvent() {
10675           var current = event, source;
10676           while (source = current.sourceEvent) { current = source; }
10677           return current;
10678         }
10679
10680         function point(node, event) {
10681           var svg = node.ownerSVGElement || node;
10682
10683           if (svg.createSVGPoint) {
10684             var point = svg.createSVGPoint();
10685             point.x = event.clientX, point.y = event.clientY;
10686             point = point.matrixTransform(node.getScreenCTM().inverse());
10687             return [point.x, point.y];
10688           }
10689
10690           var rect = node.getBoundingClientRect();
10691           return [event.clientX - rect.left - node.clientLeft, event.clientY - rect.top - node.clientTop];
10692         }
10693
10694         function mouse(node) {
10695           var event = sourceEvent();
10696           if (event.changedTouches) { event = event.changedTouches[0]; }
10697           return point(node, event);
10698         }
10699
10700         function selectAll(selector) {
10701           return typeof selector === "string"
10702               ? new Selection([document.querySelectorAll(selector)], [document.documentElement])
10703               : new Selection([selector == null ? [] : selector], root$1);
10704         }
10705
10706         function touch(node, touches, identifier) {
10707           if (arguments.length < 3) { identifier = touches, touches = sourceEvent().changedTouches; }
10708
10709           for (var i = 0, n = touches ? touches.length : 0, touch; i < n; ++i) {
10710             if ((touch = touches[i]).identifier === identifier) {
10711               return point(node, touch);
10712             }
10713           }
10714
10715           return null;
10716         }
10717
10718         function nopropagation() {
10719           event.stopImmediatePropagation();
10720         }
10721
10722         function noevent() {
10723           event.preventDefault();
10724           event.stopImmediatePropagation();
10725         }
10726
10727         function dragDisable(view) {
10728           var root = view.document.documentElement,
10729               selection = select(view).on("dragstart.drag", noevent, true);
10730           if ("onselectstart" in root) {
10731             selection.on("selectstart.drag", noevent, true);
10732           } else {
10733             root.__noselect = root.style.MozUserSelect;
10734             root.style.MozUserSelect = "none";
10735           }
10736         }
10737
10738         function yesdrag(view, noclick) {
10739           var root = view.document.documentElement,
10740               selection = select(view).on("dragstart.drag", null);
10741           if (noclick) {
10742             selection.on("click.drag", noevent, true);
10743             setTimeout(function() { selection.on("click.drag", null); }, 0);
10744           }
10745           if ("onselectstart" in root) {
10746             selection.on("selectstart.drag", null);
10747           } else {
10748             root.style.MozUserSelect = root.__noselect;
10749             delete root.__noselect;
10750           }
10751         }
10752
10753         function constant$1(x) {
10754           return function() {
10755             return x;
10756           };
10757         }
10758
10759         function DragEvent(target, type, subject, id, active, x, y, dx, dy, dispatch) {
10760           this.target = target;
10761           this.type = type;
10762           this.subject = subject;
10763           this.identifier = id;
10764           this.active = active;
10765           this.x = x;
10766           this.y = y;
10767           this.dx = dx;
10768           this.dy = dy;
10769           this._ = dispatch;
10770         }
10771
10772         DragEvent.prototype.on = function() {
10773           var value = this._.on.apply(this._, arguments);
10774           return value === this._ ? this : value;
10775         };
10776
10777         // Ignore right-click, since that should open the context menu.
10778         function defaultFilter() {
10779           return !event.ctrlKey && !event.button;
10780         }
10781
10782         function defaultContainer() {
10783           return this.parentNode;
10784         }
10785
10786         function defaultSubject(d) {
10787           return d == null ? {x: event.x, y: event.y} : d;
10788         }
10789
10790         function defaultTouchable() {
10791           return navigator.maxTouchPoints || ("ontouchstart" in this);
10792         }
10793
10794         function d3_drag() {
10795           var filter = defaultFilter,
10796               container = defaultContainer,
10797               subject = defaultSubject,
10798               touchable = defaultTouchable,
10799               gestures = {},
10800               listeners = dispatch("start", "drag", "end"),
10801               active = 0,
10802               mousedownx,
10803               mousedowny,
10804               mousemoving,
10805               touchending,
10806               clickDistance2 = 0;
10807
10808           function drag(selection) {
10809             selection
10810                 .on("mousedown.drag", mousedowned)
10811               .filter(touchable)
10812                 .on("touchstart.drag", touchstarted)
10813                 .on("touchmove.drag", touchmoved)
10814                 .on("touchend.drag touchcancel.drag", touchended)
10815                 .style("touch-action", "none")
10816                 .style("-webkit-tap-highlight-color", "rgba(0,0,0,0)");
10817           }
10818
10819           function mousedowned() {
10820             if (touchending || !filter.apply(this, arguments)) { return; }
10821             var gesture = beforestart("mouse", container.apply(this, arguments), mouse, this, arguments);
10822             if (!gesture) { return; }
10823             select(event.view).on("mousemove.drag", mousemoved, true).on("mouseup.drag", mouseupped, true);
10824             dragDisable(event.view);
10825             nopropagation();
10826             mousemoving = false;
10827             mousedownx = event.clientX;
10828             mousedowny = event.clientY;
10829             gesture("start");
10830           }
10831
10832           function mousemoved() {
10833             noevent();
10834             if (!mousemoving) {
10835               var dx = event.clientX - mousedownx, dy = event.clientY - mousedowny;
10836               mousemoving = dx * dx + dy * dy > clickDistance2;
10837             }
10838             gestures.mouse("drag");
10839           }
10840
10841           function mouseupped() {
10842             select(event.view).on("mousemove.drag mouseup.drag", null);
10843             yesdrag(event.view, mousemoving);
10844             noevent();
10845             gestures.mouse("end");
10846           }
10847
10848           function touchstarted() {
10849             var arguments$1 = arguments;
10850
10851             if (!filter.apply(this, arguments)) { return; }
10852             var touches = event.changedTouches,
10853                 c = container.apply(this, arguments),
10854                 n = touches.length, i, gesture;
10855
10856             for (i = 0; i < n; ++i) {
10857               if (gesture = beforestart(touches[i].identifier, c, touch, this, arguments$1)) {
10858                 nopropagation();
10859                 gesture("start");
10860               }
10861             }
10862           }
10863
10864           function touchmoved() {
10865             var touches = event.changedTouches,
10866                 n = touches.length, i, gesture;
10867
10868             for (i = 0; i < n; ++i) {
10869               if (gesture = gestures[touches[i].identifier]) {
10870                 noevent();
10871                 gesture("drag");
10872               }
10873             }
10874           }
10875
10876           function touchended() {
10877             var touches = event.changedTouches,
10878                 n = touches.length, i, gesture;
10879
10880             if (touchending) { clearTimeout(touchending); }
10881             touchending = setTimeout(function() { touchending = null; }, 500); // Ghost clicks are delayed!
10882             for (i = 0; i < n; ++i) {
10883               if (gesture = gestures[touches[i].identifier]) {
10884                 nopropagation();
10885                 gesture("end");
10886               }
10887             }
10888           }
10889
10890           function beforestart(id, container, point, that, args) {
10891             var p = point(container, id), s, dx, dy,
10892                 sublisteners = listeners.copy();
10893
10894             if (!customEvent(new DragEvent(drag, "beforestart", s, id, active, p[0], p[1], 0, 0, sublisteners), function() {
10895               if ((event.subject = s = subject.apply(that, args)) == null) { return false; }
10896               dx = s.x - p[0] || 0;
10897               dy = s.y - p[1] || 0;
10898               return true;
10899             })) { return; }
10900
10901             return function gesture(type) {
10902               var p0 = p, n;
10903               switch (type) {
10904                 case "start": gestures[id] = gesture, n = active++; break;
10905                 case "end": delete gestures[id], --active; // nobreak
10906                 case "drag": p = point(container, id), n = active; break;
10907               }
10908               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]);
10909             };
10910           }
10911
10912           drag.filter = function(_) {
10913             return arguments.length ? (filter = typeof _ === "function" ? _ : constant$1(!!_), drag) : filter;
10914           };
10915
10916           drag.container = function(_) {
10917             return arguments.length ? (container = typeof _ === "function" ? _ : constant$1(_), drag) : container;
10918           };
10919
10920           drag.subject = function(_) {
10921             return arguments.length ? (subject = typeof _ === "function" ? _ : constant$1(_), drag) : subject;
10922           };
10923
10924           drag.touchable = function(_) {
10925             return arguments.length ? (touchable = typeof _ === "function" ? _ : constant$1(!!_), drag) : touchable;
10926           };
10927
10928           drag.on = function() {
10929             var value = listeners.on.apply(listeners, arguments);
10930             return value === listeners ? drag : value;
10931           };
10932
10933           drag.clickDistance = function(_) {
10934             return arguments.length ? (clickDistance2 = (_ = +_) * _, drag) : Math.sqrt(clickDistance2);
10935           };
10936
10937           return drag;
10938         }
10939
10940         function define$1(constructor, factory, prototype) {
10941           constructor.prototype = factory.prototype = prototype;
10942           prototype.constructor = constructor;
10943         }
10944
10945         function extend(parent, definition) {
10946           var prototype = Object.create(parent.prototype);
10947           for (var key in definition) { prototype[key] = definition[key]; }
10948           return prototype;
10949         }
10950
10951         function Color() {}
10952
10953         var darker = 0.7;
10954         var brighter = 1 / darker;
10955
10956         var reI = "\\s*([+-]?\\d+)\\s*",
10957             reN = "\\s*([+-]?\\d*\\.?\\d+(?:[eE][+-]?\\d+)?)\\s*",
10958             reP = "\\s*([+-]?\\d*\\.?\\d+(?:[eE][+-]?\\d+)?)%\\s*",
10959             reHex = /^#([0-9a-f]{3,8})$/,
10960             reRgbInteger = new RegExp("^rgb\\(" + [reI, reI, reI] + "\\)$"),
10961             reRgbPercent = new RegExp("^rgb\\(" + [reP, reP, reP] + "\\)$"),
10962             reRgbaInteger = new RegExp("^rgba\\(" + [reI, reI, reI, reN] + "\\)$"),
10963             reRgbaPercent = new RegExp("^rgba\\(" + [reP, reP, reP, reN] + "\\)$"),
10964             reHslPercent = new RegExp("^hsl\\(" + [reN, reP, reP] + "\\)$"),
10965             reHslaPercent = new RegExp("^hsla\\(" + [reN, reP, reP, reN] + "\\)$");
10966
10967         var named = {
10968           aliceblue: 0xf0f8ff,
10969           antiquewhite: 0xfaebd7,
10970           aqua: 0x00ffff,
10971           aquamarine: 0x7fffd4,
10972           azure: 0xf0ffff,
10973           beige: 0xf5f5dc,
10974           bisque: 0xffe4c4,
10975           black: 0x000000,
10976           blanchedalmond: 0xffebcd,
10977           blue: 0x0000ff,
10978           blueviolet: 0x8a2be2,
10979           brown: 0xa52a2a,
10980           burlywood: 0xdeb887,
10981           cadetblue: 0x5f9ea0,
10982           chartreuse: 0x7fff00,
10983           chocolate: 0xd2691e,
10984           coral: 0xff7f50,
10985           cornflowerblue: 0x6495ed,
10986           cornsilk: 0xfff8dc,
10987           crimson: 0xdc143c,
10988           cyan: 0x00ffff,
10989           darkblue: 0x00008b,
10990           darkcyan: 0x008b8b,
10991           darkgoldenrod: 0xb8860b,
10992           darkgray: 0xa9a9a9,
10993           darkgreen: 0x006400,
10994           darkgrey: 0xa9a9a9,
10995           darkkhaki: 0xbdb76b,
10996           darkmagenta: 0x8b008b,
10997           darkolivegreen: 0x556b2f,
10998           darkorange: 0xff8c00,
10999           darkorchid: 0x9932cc,
11000           darkred: 0x8b0000,
11001           darksalmon: 0xe9967a,
11002           darkseagreen: 0x8fbc8f,
11003           darkslateblue: 0x483d8b,
11004           darkslategray: 0x2f4f4f,
11005           darkslategrey: 0x2f4f4f,
11006           darkturquoise: 0x00ced1,
11007           darkviolet: 0x9400d3,
11008           deeppink: 0xff1493,
11009           deepskyblue: 0x00bfff,
11010           dimgray: 0x696969,
11011           dimgrey: 0x696969,
11012           dodgerblue: 0x1e90ff,
11013           firebrick: 0xb22222,
11014           floralwhite: 0xfffaf0,
11015           forestgreen: 0x228b22,
11016           fuchsia: 0xff00ff,
11017           gainsboro: 0xdcdcdc,
11018           ghostwhite: 0xf8f8ff,
11019           gold: 0xffd700,
11020           goldenrod: 0xdaa520,
11021           gray: 0x808080,
11022           green: 0x008000,
11023           greenyellow: 0xadff2f,
11024           grey: 0x808080,
11025           honeydew: 0xf0fff0,
11026           hotpink: 0xff69b4,
11027           indianred: 0xcd5c5c,
11028           indigo: 0x4b0082,
11029           ivory: 0xfffff0,
11030           khaki: 0xf0e68c,
11031           lavender: 0xe6e6fa,
11032           lavenderblush: 0xfff0f5,
11033           lawngreen: 0x7cfc00,
11034           lemonchiffon: 0xfffacd,
11035           lightblue: 0xadd8e6,
11036           lightcoral: 0xf08080,
11037           lightcyan: 0xe0ffff,
11038           lightgoldenrodyellow: 0xfafad2,
11039           lightgray: 0xd3d3d3,
11040           lightgreen: 0x90ee90,
11041           lightgrey: 0xd3d3d3,
11042           lightpink: 0xffb6c1,
11043           lightsalmon: 0xffa07a,
11044           lightseagreen: 0x20b2aa,
11045           lightskyblue: 0x87cefa,
11046           lightslategray: 0x778899,
11047           lightslategrey: 0x778899,
11048           lightsteelblue: 0xb0c4de,
11049           lightyellow: 0xffffe0,
11050           lime: 0x00ff00,
11051           limegreen: 0x32cd32,
11052           linen: 0xfaf0e6,
11053           magenta: 0xff00ff,
11054           maroon: 0x800000,
11055           mediumaquamarine: 0x66cdaa,
11056           mediumblue: 0x0000cd,
11057           mediumorchid: 0xba55d3,
11058           mediumpurple: 0x9370db,
11059           mediumseagreen: 0x3cb371,
11060           mediumslateblue: 0x7b68ee,
11061           mediumspringgreen: 0x00fa9a,
11062           mediumturquoise: 0x48d1cc,
11063           mediumvioletred: 0xc71585,
11064           midnightblue: 0x191970,
11065           mintcream: 0xf5fffa,
11066           mistyrose: 0xffe4e1,
11067           moccasin: 0xffe4b5,
11068           navajowhite: 0xffdead,
11069           navy: 0x000080,
11070           oldlace: 0xfdf5e6,
11071           olive: 0x808000,
11072           olivedrab: 0x6b8e23,
11073           orange: 0xffa500,
11074           orangered: 0xff4500,
11075           orchid: 0xda70d6,
11076           palegoldenrod: 0xeee8aa,
11077           palegreen: 0x98fb98,
11078           paleturquoise: 0xafeeee,
11079           palevioletred: 0xdb7093,
11080           papayawhip: 0xffefd5,
11081           peachpuff: 0xffdab9,
11082           peru: 0xcd853f,
11083           pink: 0xffc0cb,
11084           plum: 0xdda0dd,
11085           powderblue: 0xb0e0e6,
11086           purple: 0x800080,
11087           rebeccapurple: 0x663399,
11088           red: 0xff0000,
11089           rosybrown: 0xbc8f8f,
11090           royalblue: 0x4169e1,
11091           saddlebrown: 0x8b4513,
11092           salmon: 0xfa8072,
11093           sandybrown: 0xf4a460,
11094           seagreen: 0x2e8b57,
11095           seashell: 0xfff5ee,
11096           sienna: 0xa0522d,
11097           silver: 0xc0c0c0,
11098           skyblue: 0x87ceeb,
11099           slateblue: 0x6a5acd,
11100           slategray: 0x708090,
11101           slategrey: 0x708090,
11102           snow: 0xfffafa,
11103           springgreen: 0x00ff7f,
11104           steelblue: 0x4682b4,
11105           tan: 0xd2b48c,
11106           teal: 0x008080,
11107           thistle: 0xd8bfd8,
11108           tomato: 0xff6347,
11109           turquoise: 0x40e0d0,
11110           violet: 0xee82ee,
11111           wheat: 0xf5deb3,
11112           white: 0xffffff,
11113           whitesmoke: 0xf5f5f5,
11114           yellow: 0xffff00,
11115           yellowgreen: 0x9acd32
11116         };
11117
11118         define$1(Color, color, {
11119           copy: function(channels) {
11120             return Object.assign(new this.constructor, this, channels);
11121           },
11122           displayable: function() {
11123             return this.rgb().displayable();
11124           },
11125           hex: color_formatHex, // Deprecated! Use color.formatHex.
11126           formatHex: color_formatHex,
11127           formatHsl: color_formatHsl,
11128           formatRgb: color_formatRgb,
11129           toString: color_formatRgb
11130         });
11131
11132         function color_formatHex() {
11133           return this.rgb().formatHex();
11134         }
11135
11136         function color_formatHsl() {
11137           return hslConvert(this).formatHsl();
11138         }
11139
11140         function color_formatRgb() {
11141           return this.rgb().formatRgb();
11142         }
11143
11144         function color(format) {
11145           var m, l;
11146           format = (format + "").trim().toLowerCase();
11147           return (m = reHex.exec(format)) ? (l = m[1].length, m = parseInt(m[1], 16), l === 6 ? rgbn(m) // #ff0000
11148               : l === 3 ? new Rgb((m >> 8 & 0xf) | (m >> 4 & 0xf0), (m >> 4 & 0xf) | (m & 0xf0), ((m & 0xf) << 4) | (m & 0xf), 1) // #f00
11149               : l === 8 ? rgba(m >> 24 & 0xff, m >> 16 & 0xff, m >> 8 & 0xff, (m & 0xff) / 0xff) // #ff000000
11150               : 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
11151               : null) // invalid hex
11152               : (m = reRgbInteger.exec(format)) ? new Rgb(m[1], m[2], m[3], 1) // rgb(255, 0, 0)
11153               : (m = reRgbPercent.exec(format)) ? new Rgb(m[1] * 255 / 100, m[2] * 255 / 100, m[3] * 255 / 100, 1) // rgb(100%, 0%, 0%)
11154               : (m = reRgbaInteger.exec(format)) ? rgba(m[1], m[2], m[3], m[4]) // rgba(255, 0, 0, 1)
11155               : (m = reRgbaPercent.exec(format)) ? rgba(m[1] * 255 / 100, m[2] * 255 / 100, m[3] * 255 / 100, m[4]) // rgb(100%, 0%, 0%, 1)
11156               : (m = reHslPercent.exec(format)) ? hsla(m[1], m[2] / 100, m[3] / 100, 1) // hsl(120, 50%, 50%)
11157               : (m = reHslaPercent.exec(format)) ? hsla(m[1], m[2] / 100, m[3] / 100, m[4]) // hsla(120, 50%, 50%, 1)
11158               : named.hasOwnProperty(format) ? rgbn(named[format]) // eslint-disable-line no-prototype-builtins
11159               : format === "transparent" ? new Rgb(NaN, NaN, NaN, 0)
11160               : null;
11161         }
11162
11163         function rgbn(n) {
11164           return new Rgb(n >> 16 & 0xff, n >> 8 & 0xff, n & 0xff, 1);
11165         }
11166
11167         function rgba(r, g, b, a) {
11168           if (a <= 0) { r = g = b = NaN; }
11169           return new Rgb(r, g, b, a);
11170         }
11171
11172         function rgbConvert(o) {
11173           if (!(o instanceof Color)) { o = color(o); }
11174           if (!o) { return new Rgb; }
11175           o = o.rgb();
11176           return new Rgb(o.r, o.g, o.b, o.opacity);
11177         }
11178
11179         function rgb(r, g, b, opacity) {
11180           return arguments.length === 1 ? rgbConvert(r) : new Rgb(r, g, b, opacity == null ? 1 : opacity);
11181         }
11182
11183         function Rgb(r, g, b, opacity) {
11184           this.r = +r;
11185           this.g = +g;
11186           this.b = +b;
11187           this.opacity = +opacity;
11188         }
11189
11190         define$1(Rgb, rgb, extend(Color, {
11191           brighter: function(k) {
11192             k = k == null ? brighter : Math.pow(brighter, k);
11193             return new Rgb(this.r * k, this.g * k, this.b * k, this.opacity);
11194           },
11195           darker: function(k) {
11196             k = k == null ? darker : Math.pow(darker, k);
11197             return new Rgb(this.r * k, this.g * k, this.b * k, this.opacity);
11198           },
11199           rgb: function() {
11200             return this;
11201           },
11202           displayable: function() {
11203             return (-0.5 <= this.r && this.r < 255.5)
11204                 && (-0.5 <= this.g && this.g < 255.5)
11205                 && (-0.5 <= this.b && this.b < 255.5)
11206                 && (0 <= this.opacity && this.opacity <= 1);
11207           },
11208           hex: rgb_formatHex, // Deprecated! Use color.formatHex.
11209           formatHex: rgb_formatHex,
11210           formatRgb: rgb_formatRgb,
11211           toString: rgb_formatRgb
11212         }));
11213
11214         function rgb_formatHex() {
11215           return "#" + hex$1(this.r) + hex$1(this.g) + hex$1(this.b);
11216         }
11217
11218         function rgb_formatRgb() {
11219           var a = this.opacity; a = isNaN(a) ? 1 : Math.max(0, Math.min(1, a));
11220           return (a === 1 ? "rgb(" : "rgba(")
11221               + Math.max(0, Math.min(255, Math.round(this.r) || 0)) + ", "
11222               + Math.max(0, Math.min(255, Math.round(this.g) || 0)) + ", "
11223               + Math.max(0, Math.min(255, Math.round(this.b) || 0))
11224               + (a === 1 ? ")" : ", " + a + ")");
11225         }
11226
11227         function hex$1(value) {
11228           value = Math.max(0, Math.min(255, Math.round(value) || 0));
11229           return (value < 16 ? "0" : "") + value.toString(16);
11230         }
11231
11232         function hsla(h, s, l, a) {
11233           if (a <= 0) { h = s = l = NaN; }
11234           else if (l <= 0 || l >= 1) { h = s = NaN; }
11235           else if (s <= 0) { h = NaN; }
11236           return new Hsl(h, s, l, a);
11237         }
11238
11239         function hslConvert(o) {
11240           if (o instanceof Hsl) { return new Hsl(o.h, o.s, o.l, o.opacity); }
11241           if (!(o instanceof Color)) { o = color(o); }
11242           if (!o) { return new Hsl; }
11243           if (o instanceof Hsl) { return o; }
11244           o = o.rgb();
11245           var r = o.r / 255,
11246               g = o.g / 255,
11247               b = o.b / 255,
11248               min = Math.min(r, g, b),
11249               max = Math.max(r, g, b),
11250               h = NaN,
11251               s = max - min,
11252               l = (max + min) / 2;
11253           if (s) {
11254             if (r === max) { h = (g - b) / s + (g < b) * 6; }
11255             else if (g === max) { h = (b - r) / s + 2; }
11256             else { h = (r - g) / s + 4; }
11257             s /= l < 0.5 ? max + min : 2 - max - min;
11258             h *= 60;
11259           } else {
11260             s = l > 0 && l < 1 ? 0 : h;
11261           }
11262           return new Hsl(h, s, l, o.opacity);
11263         }
11264
11265         function hsl(h, s, l, opacity) {
11266           return arguments.length === 1 ? hslConvert(h) : new Hsl(h, s, l, opacity == null ? 1 : opacity);
11267         }
11268
11269         function Hsl(h, s, l, opacity) {
11270           this.h = +h;
11271           this.s = +s;
11272           this.l = +l;
11273           this.opacity = +opacity;
11274         }
11275
11276         define$1(Hsl, hsl, extend(Color, {
11277           brighter: function(k) {
11278             k = k == null ? brighter : Math.pow(brighter, k);
11279             return new Hsl(this.h, this.s, this.l * k, this.opacity);
11280           },
11281           darker: function(k) {
11282             k = k == null ? darker : Math.pow(darker, k);
11283             return new Hsl(this.h, this.s, this.l * k, this.opacity);
11284           },
11285           rgb: function() {
11286             var h = this.h % 360 + (this.h < 0) * 360,
11287                 s = isNaN(h) || isNaN(this.s) ? 0 : this.s,
11288                 l = this.l,
11289                 m2 = l + (l < 0.5 ? l : 1 - l) * s,
11290                 m1 = 2 * l - m2;
11291             return new Rgb(
11292               hsl2rgb(h >= 240 ? h - 240 : h + 120, m1, m2),
11293               hsl2rgb(h, m1, m2),
11294               hsl2rgb(h < 120 ? h + 240 : h - 120, m1, m2),
11295               this.opacity
11296             );
11297           },
11298           displayable: function() {
11299             return (0 <= this.s && this.s <= 1 || isNaN(this.s))
11300                 && (0 <= this.l && this.l <= 1)
11301                 && (0 <= this.opacity && this.opacity <= 1);
11302           },
11303           formatHsl: function() {
11304             var a = this.opacity; a = isNaN(a) ? 1 : Math.max(0, Math.min(1, a));
11305             return (a === 1 ? "hsl(" : "hsla(")
11306                 + (this.h || 0) + ", "
11307                 + (this.s || 0) * 100 + "%, "
11308                 + (this.l || 0) * 100 + "%"
11309                 + (a === 1 ? ")" : ", " + a + ")");
11310           }
11311         }));
11312
11313         /* From FvD 13.37, CSS Color Module Level 3 */
11314         function hsl2rgb(h, m1, m2) {
11315           return (h < 60 ? m1 + (m2 - m1) * h / 60
11316               : h < 180 ? m2
11317               : h < 240 ? m1 + (m2 - m1) * (240 - h) / 60
11318               : m1) * 255;
11319         }
11320
11321         function constant$2(x) {
11322           return function() {
11323             return x;
11324           };
11325         }
11326
11327         function linear(a, d) {
11328           return function(t) {
11329             return a + t * d;
11330           };
11331         }
11332
11333         function exponential(a, b, y) {
11334           return a = Math.pow(a, y), b = Math.pow(b, y) - a, y = 1 / y, function(t) {
11335             return Math.pow(a + t * b, y);
11336           };
11337         }
11338
11339         function gamma(y) {
11340           return (y = +y) === 1 ? nogamma : function(a, b) {
11341             return b - a ? exponential(a, b, y) : constant$2(isNaN(a) ? b : a);
11342           };
11343         }
11344
11345         function nogamma(a, b) {
11346           var d = b - a;
11347           return d ? linear(a, d) : constant$2(isNaN(a) ? b : a);
11348         }
11349
11350         var d3_interpolateRgb = (function rgbGamma(y) {
11351           var color = gamma(y);
11352
11353           function rgb$1(start, end) {
11354             var r = color((start = rgb(start)).r, (end = rgb(end)).r),
11355                 g = color(start.g, end.g),
11356                 b = color(start.b, end.b),
11357                 opacity = nogamma(start.opacity, end.opacity);
11358             return function(t) {
11359               start.r = r(t);
11360               start.g = g(t);
11361               start.b = b(t);
11362               start.opacity = opacity(t);
11363               return start + "";
11364             };
11365           }
11366
11367           rgb$1.gamma = rgbGamma;
11368
11369           return rgb$1;
11370         })(1);
11371
11372         function numberArray(a, b) {
11373           if (!b) { b = []; }
11374           var n = a ? Math.min(b.length, a.length) : 0,
11375               c = b.slice(),
11376               i;
11377           return function(t) {
11378             for (i = 0; i < n; ++i) { c[i] = a[i] * (1 - t) + b[i] * t; }
11379             return c;
11380           };
11381         }
11382
11383         function isNumberArray(x) {
11384           return ArrayBuffer.isView(x) && !(x instanceof DataView);
11385         }
11386
11387         function genericArray(a, b) {
11388           var nb = b ? b.length : 0,
11389               na = a ? Math.min(nb, a.length) : 0,
11390               x = new Array(na),
11391               c = new Array(nb),
11392               i;
11393
11394           for (i = 0; i < na; ++i) { x[i] = interpolate(a[i], b[i]); }
11395           for (; i < nb; ++i) { c[i] = b[i]; }
11396
11397           return function(t) {
11398             for (i = 0; i < na; ++i) { c[i] = x[i](t); }
11399             return c;
11400           };
11401         }
11402
11403         function date(a, b) {
11404           var d = new Date;
11405           return a = +a, b = +b, function(t) {
11406             return d.setTime(a * (1 - t) + b * t), d;
11407           };
11408         }
11409
11410         function d3_interpolateNumber(a, b) {
11411           return a = +a, b = +b, function(t) {
11412             return a * (1 - t) + b * t;
11413           };
11414         }
11415
11416         function object(a, b) {
11417           var i = {},
11418               c = {},
11419               k;
11420
11421           if (a === null || typeof a !== "object") { a = {}; }
11422           if (b === null || typeof b !== "object") { b = {}; }
11423
11424           for (k in b) {
11425             if (k in a) {
11426               i[k] = interpolate(a[k], b[k]);
11427             } else {
11428               c[k] = b[k];
11429             }
11430           }
11431
11432           return function(t) {
11433             for (k in i) { c[k] = i[k](t); }
11434             return c;
11435           };
11436         }
11437
11438         var reA = /[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g,
11439             reB = new RegExp(reA.source, "g");
11440
11441         function zero(b) {
11442           return function() {
11443             return b;
11444           };
11445         }
11446
11447         function one(b) {
11448           return function(t) {
11449             return b(t) + "";
11450           };
11451         }
11452
11453         function interpolateString(a, b) {
11454           var bi = reA.lastIndex = reB.lastIndex = 0, // scan index for next number in b
11455               am, // current match in a
11456               bm, // current match in b
11457               bs, // string preceding current number in b, if any
11458               i = -1, // index in s
11459               s = [], // string constants and placeholders
11460               q = []; // number interpolators
11461
11462           // Coerce inputs to strings.
11463           a = a + "", b = b + "";
11464
11465           // Interpolate pairs of numbers in a & b.
11466           while ((am = reA.exec(a))
11467               && (bm = reB.exec(b))) {
11468             if ((bs = bm.index) > bi) { // a string precedes the next number in b
11469               bs = b.slice(bi, bs);
11470               if (s[i]) { s[i] += bs; } // coalesce with previous string
11471               else { s[++i] = bs; }
11472             }
11473             if ((am = am[0]) === (bm = bm[0])) { // numbers in a & b match
11474               if (s[i]) { s[i] += bm; } // coalesce with previous string
11475               else { s[++i] = bm; }
11476             } else { // interpolate non-matching numbers
11477               s[++i] = null;
11478               q.push({i: i, x: d3_interpolateNumber(am, bm)});
11479             }
11480             bi = reB.lastIndex;
11481           }
11482
11483           // Add remains of b.
11484           if (bi < b.length) {
11485             bs = b.slice(bi);
11486             if (s[i]) { s[i] += bs; } // coalesce with previous string
11487             else { s[++i] = bs; }
11488           }
11489
11490           // Special optimization for only a single match.
11491           // Otherwise, interpolate each of the numbers and rejoin the string.
11492           return s.length < 2 ? (q[0]
11493               ? one(q[0].x)
11494               : zero(b))
11495               : (b = q.length, function(t) {
11496                   for (var i = 0, o; i < b; ++i) { s[(o = q[i]).i] = o.x(t); }
11497                   return s.join("");
11498                 });
11499         }
11500
11501         function interpolate(a, b) {
11502           var t = typeof b, c;
11503           return b == null || t === "boolean" ? constant$2(b)
11504               : (t === "number" ? d3_interpolateNumber
11505               : t === "string" ? ((c = color(b)) ? (b = c, d3_interpolateRgb) : interpolateString)
11506               : b instanceof color ? d3_interpolateRgb
11507               : b instanceof Date ? date
11508               : isNumberArray(b) ? numberArray
11509               : Array.isArray(b) ? genericArray
11510               : typeof b.valueOf !== "function" && typeof b.toString !== "function" || isNaN(b) ? object
11511               : d3_interpolateNumber)(a, b);
11512         }
11513
11514         function interpolateRound(a, b) {
11515           return a = +a, b = +b, function(t) {
11516             return Math.round(a * (1 - t) + b * t);
11517           };
11518         }
11519
11520         var degrees$1 = 180 / Math.PI;
11521
11522         var identity$1 = {
11523           translateX: 0,
11524           translateY: 0,
11525           rotate: 0,
11526           skewX: 0,
11527           scaleX: 1,
11528           scaleY: 1
11529         };
11530
11531         function decompose(a, b, c, d, e, f) {
11532           var scaleX, scaleY, skewX;
11533           if (scaleX = Math.sqrt(a * a + b * b)) { a /= scaleX, b /= scaleX; }
11534           if (skewX = a * c + b * d) { c -= a * skewX, d -= b * skewX; }
11535           if (scaleY = Math.sqrt(c * c + d * d)) { c /= scaleY, d /= scaleY, skewX /= scaleY; }
11536           if (a * d < b * c) { a = -a, b = -b, skewX = -skewX, scaleX = -scaleX; }
11537           return {
11538             translateX: e,
11539             translateY: f,
11540             rotate: Math.atan2(b, a) * degrees$1,
11541             skewX: Math.atan(skewX) * degrees$1,
11542             scaleX: scaleX,
11543             scaleY: scaleY
11544           };
11545         }
11546
11547         var cssNode,
11548             cssRoot,
11549             cssView,
11550             svgNode;
11551
11552         function parseCss(value) {
11553           if (value === "none") { return identity$1; }
11554           if (!cssNode) { cssNode = document.createElement("DIV"), cssRoot = document.documentElement, cssView = document.defaultView; }
11555           cssNode.style.transform = value;
11556           value = cssView.getComputedStyle(cssRoot.appendChild(cssNode), null).getPropertyValue("transform");
11557           cssRoot.removeChild(cssNode);
11558           value = value.slice(7, -1).split(",");
11559           return decompose(+value[0], +value[1], +value[2], +value[3], +value[4], +value[5]);
11560         }
11561
11562         function parseSvg(value) {
11563           if (value == null) { return identity$1; }
11564           if (!svgNode) { svgNode = document.createElementNS("http://www.w3.org/2000/svg", "g"); }
11565           svgNode.setAttribute("transform", value);
11566           if (!(value = svgNode.transform.baseVal.consolidate())) { return identity$1; }
11567           value = value.matrix;
11568           return decompose(value.a, value.b, value.c, value.d, value.e, value.f);
11569         }
11570
11571         function interpolateTransform(parse, pxComma, pxParen, degParen) {
11572
11573           function pop(s) {
11574             return s.length ? s.pop() + " " : "";
11575           }
11576
11577           function translate(xa, ya, xb, yb, s, q) {
11578             if (xa !== xb || ya !== yb) {
11579               var i = s.push("translate(", null, pxComma, null, pxParen);
11580               q.push({i: i - 4, x: d3_interpolateNumber(xa, xb)}, {i: i - 2, x: d3_interpolateNumber(ya, yb)});
11581             } else if (xb || yb) {
11582               s.push("translate(" + xb + pxComma + yb + pxParen);
11583             }
11584           }
11585
11586           function rotate(a, b, s, q) {
11587             if (a !== b) {
11588               if (a - b > 180) { b += 360; } else if (b - a > 180) { a += 360; } // shortest path
11589               q.push({i: s.push(pop(s) + "rotate(", null, degParen) - 2, x: d3_interpolateNumber(a, b)});
11590             } else if (b) {
11591               s.push(pop(s) + "rotate(" + b + degParen);
11592             }
11593           }
11594
11595           function skewX(a, b, s, q) {
11596             if (a !== b) {
11597               q.push({i: s.push(pop(s) + "skewX(", null, degParen) - 2, x: d3_interpolateNumber(a, b)});
11598             } else if (b) {
11599               s.push(pop(s) + "skewX(" + b + degParen);
11600             }
11601           }
11602
11603           function scale(xa, ya, xb, yb, s, q) {
11604             if (xa !== xb || ya !== yb) {
11605               var i = s.push(pop(s) + "scale(", null, ",", null, ")");
11606               q.push({i: i - 4, x: d3_interpolateNumber(xa, xb)}, {i: i - 2, x: d3_interpolateNumber(ya, yb)});
11607             } else if (xb !== 1 || yb !== 1) {
11608               s.push(pop(s) + "scale(" + xb + "," + yb + ")");
11609             }
11610           }
11611
11612           return function(a, b) {
11613             var s = [], // string constants and placeholders
11614                 q = []; // number interpolators
11615             a = parse(a), b = parse(b);
11616             translate(a.translateX, a.translateY, b.translateX, b.translateY, s, q);
11617             rotate(a.rotate, b.rotate, s, q);
11618             skewX(a.skewX, b.skewX, s, q);
11619             scale(a.scaleX, a.scaleY, b.scaleX, b.scaleY, s, q);
11620             a = b = null; // gc
11621             return function(t) {
11622               var i = -1, n = q.length, o;
11623               while (++i < n) { s[(o = q[i]).i] = o.x(t); }
11624               return s.join("");
11625             };
11626           };
11627         }
11628
11629         var interpolateTransformCss = interpolateTransform(parseCss, "px, ", "px)", "deg)");
11630         var interpolateTransformSvg = interpolateTransform(parseSvg, ", ", ")", ")");
11631
11632         var rho = Math.SQRT2,
11633             rho2 = 2,
11634             rho4 = 4,
11635             epsilon2$1 = 1e-12;
11636
11637         function cosh(x) {
11638           return ((x = Math.exp(x)) + 1 / x) / 2;
11639         }
11640
11641         function sinh(x) {
11642           return ((x = Math.exp(x)) - 1 / x) / 2;
11643         }
11644
11645         function tanh(x) {
11646           return ((x = Math.exp(2 * x)) - 1) / (x + 1);
11647         }
11648
11649         // p0 = [ux0, uy0, w0]
11650         // p1 = [ux1, uy1, w1]
11651         function interpolateZoom(p0, p1) {
11652           var ux0 = p0[0], uy0 = p0[1], w0 = p0[2],
11653               ux1 = p1[0], uy1 = p1[1], w1 = p1[2],
11654               dx = ux1 - ux0,
11655               dy = uy1 - uy0,
11656               d2 = dx * dx + dy * dy,
11657               i,
11658               S;
11659
11660           // Special case for u0 ≅ u1.
11661           if (d2 < epsilon2$1) {
11662             S = Math.log(w1 / w0) / rho;
11663             i = function(t) {
11664               return [
11665                 ux0 + t * dx,
11666                 uy0 + t * dy,
11667                 w0 * Math.exp(rho * t * S)
11668               ];
11669             };
11670           }
11671
11672           // General case.
11673           else {
11674             var d1 = Math.sqrt(d2),
11675                 b0 = (w1 * w1 - w0 * w0 + rho4 * d2) / (2 * w0 * rho2 * d1),
11676                 b1 = (w1 * w1 - w0 * w0 - rho4 * d2) / (2 * w1 * rho2 * d1),
11677                 r0 = Math.log(Math.sqrt(b0 * b0 + 1) - b0),
11678                 r1 = Math.log(Math.sqrt(b1 * b1 + 1) - b1);
11679             S = (r1 - r0) / rho;
11680             i = function(t) {
11681               var s = t * S,
11682                   coshr0 = cosh(r0),
11683                   u = w0 / (rho2 * d1) * (coshr0 * tanh(rho * s + r0) - sinh(r0));
11684               return [
11685                 ux0 + u * dx,
11686                 uy0 + u * dy,
11687                 w0 * coshr0 / cosh(rho * s + r0)
11688               ];
11689             };
11690           }
11691
11692           i.duration = S * 1000;
11693
11694           return i;
11695         }
11696
11697         function d3_quantize(interpolator, n) {
11698           var samples = new Array(n);
11699           for (var i = 0; i < n; ++i) { samples[i] = interpolator(i / (n - 1)); }
11700           return samples;
11701         }
11702
11703         var frame = 0, // is an animation frame pending?
11704             timeout = 0, // is a timeout pending?
11705             interval = 0, // are any timers active?
11706             pokeDelay = 1000, // how frequently we check for clock skew
11707             taskHead,
11708             taskTail,
11709             clockLast = 0,
11710             clockNow = 0,
11711             clockSkew = 0,
11712             clock = typeof performance === "object" && performance.now ? performance : Date,
11713             setFrame = typeof window === "object" && window.requestAnimationFrame ? window.requestAnimationFrame.bind(window) : function(f) { setTimeout(f, 17); };
11714
11715         function now() {
11716           return clockNow || (setFrame(clearNow), clockNow = clock.now() + clockSkew);
11717         }
11718
11719         function clearNow() {
11720           clockNow = 0;
11721         }
11722
11723         function Timer() {
11724           this._call =
11725           this._time =
11726           this._next = null;
11727         }
11728
11729         Timer.prototype = timer.prototype = {
11730           constructor: Timer,
11731           restart: function(callback, delay, time) {
11732             if (typeof callback !== "function") { throw new TypeError("callback is not a function"); }
11733             time = (time == null ? now() : +time) + (delay == null ? 0 : +delay);
11734             if (!this._next && taskTail !== this) {
11735               if (taskTail) { taskTail._next = this; }
11736               else { taskHead = this; }
11737               taskTail = this;
11738             }
11739             this._call = callback;
11740             this._time = time;
11741             sleep();
11742           },
11743           stop: function() {
11744             if (this._call) {
11745               this._call = null;
11746               this._time = Infinity;
11747               sleep();
11748             }
11749           }
11750         };
11751
11752         function timer(callback, delay, time) {
11753           var t = new Timer;
11754           t.restart(callback, delay, time);
11755           return t;
11756         }
11757
11758         function timerFlush() {
11759           now(); // Get the current time, if not already set.
11760           ++frame; // Pretend we’ve set an alarm, if we haven’t already.
11761           var t = taskHead, e;
11762           while (t) {
11763             if ((e = clockNow - t._time) >= 0) { t._call.call(null, e); }
11764             t = t._next;
11765           }
11766           --frame;
11767         }
11768
11769         function wake() {
11770           clockNow = (clockLast = clock.now()) + clockSkew;
11771           frame = timeout = 0;
11772           try {
11773             timerFlush();
11774           } finally {
11775             frame = 0;
11776             nap();
11777             clockNow = 0;
11778           }
11779         }
11780
11781         function poke() {
11782           var now = clock.now(), delay = now - clockLast;
11783           if (delay > pokeDelay) { clockSkew -= delay, clockLast = now; }
11784         }
11785
11786         function nap() {
11787           var t0, t1 = taskHead, t2, time = Infinity;
11788           while (t1) {
11789             if (t1._call) {
11790               if (time > t1._time) { time = t1._time; }
11791               t0 = t1, t1 = t1._next;
11792             } else {
11793               t2 = t1._next, t1._next = null;
11794               t1 = t0 ? t0._next = t2 : taskHead = t2;
11795             }
11796           }
11797           taskTail = t0;
11798           sleep(time);
11799         }
11800
11801         function sleep(time) {
11802           if (frame) { return; } // Soonest alarm already set, or will be.
11803           if (timeout) { timeout = clearTimeout(timeout); }
11804           var delay = time - clockNow; // Strictly less than if we recomputed clockNow.
11805           if (delay > 24) {
11806             if (time < Infinity) { timeout = setTimeout(wake, time - clock.now() - clockSkew); }
11807             if (interval) { interval = clearInterval(interval); }
11808           } else {
11809             if (!interval) { clockLast = clock.now(), interval = setInterval(poke, pokeDelay); }
11810             frame = 1, setFrame(wake);
11811           }
11812         }
11813
11814         function d3_timeout(callback, delay, time) {
11815           var t = new Timer;
11816           delay = delay == null ? 0 : +delay;
11817           t.restart(function(elapsed) {
11818             t.stop();
11819             callback(elapsed + delay);
11820           }, delay, time);
11821           return t;
11822         }
11823
11824         var emptyOn = dispatch("start", "end", "cancel", "interrupt");
11825         var emptyTween = [];
11826
11827         var CREATED = 0;
11828         var SCHEDULED = 1;
11829         var STARTING = 2;
11830         var STARTED = 3;
11831         var RUNNING = 4;
11832         var ENDING = 5;
11833         var ENDED = 6;
11834
11835         function schedule(node, name, id, index, group, timing) {
11836           var schedules = node.__transition;
11837           if (!schedules) { node.__transition = {}; }
11838           else if (id in schedules) { return; }
11839           create$7(node, id, {
11840             name: name,
11841             index: index, // For context during callback.
11842             group: group, // For context during callback.
11843             on: emptyOn,
11844             tween: emptyTween,
11845             time: timing.time,
11846             delay: timing.delay,
11847             duration: timing.duration,
11848             ease: timing.ease,
11849             timer: null,
11850             state: CREATED
11851           });
11852         }
11853
11854         function init(node, id) {
11855           var schedule = get$2(node, id);
11856           if (schedule.state > CREATED) { throw new Error("too late; already scheduled"); }
11857           return schedule;
11858         }
11859
11860         function set$1(node, id) {
11861           var schedule = get$2(node, id);
11862           if (schedule.state > STARTED) { throw new Error("too late; already running"); }
11863           return schedule;
11864         }
11865
11866         function get$2(node, id) {
11867           var schedule = node.__transition;
11868           if (!schedule || !(schedule = schedule[id])) { throw new Error("transition not found"); }
11869           return schedule;
11870         }
11871
11872         function create$7(node, id, self) {
11873           var schedules = node.__transition,
11874               tween;
11875
11876           // Initialize the self timer when the transition is created.
11877           // Note the actual delay is not known until the first callback!
11878           schedules[id] = self;
11879           self.timer = timer(schedule, 0, self.time);
11880
11881           function schedule(elapsed) {
11882             self.state = SCHEDULED;
11883             self.timer.restart(start, self.delay, self.time);
11884
11885             // If the elapsed delay is less than our first sleep, start immediately.
11886             if (self.delay <= elapsed) { start(elapsed - self.delay); }
11887           }
11888
11889           function start(elapsed) {
11890             var i, j, n, o;
11891
11892             // If the state is not SCHEDULED, then we previously errored on start.
11893             if (self.state !== SCHEDULED) { return stop(); }
11894
11895             for (i in schedules) {
11896               o = schedules[i];
11897               if (o.name !== self.name) { continue; }
11898
11899               // While this element already has a starting transition during this frame,
11900               // defer starting an interrupting transition until that transition has a
11901               // chance to tick (and possibly end); see d3/d3-transition#54!
11902               if (o.state === STARTED) { return d3_timeout(start); }
11903
11904               // Interrupt the active transition, if any.
11905               if (o.state === RUNNING) {
11906                 o.state = ENDED;
11907                 o.timer.stop();
11908                 o.on.call("interrupt", node, node.__data__, o.index, o.group);
11909                 delete schedules[i];
11910               }
11911
11912               // Cancel any pre-empted transitions.
11913               else if (+i < id) {
11914                 o.state = ENDED;
11915                 o.timer.stop();
11916                 o.on.call("cancel", node, node.__data__, o.index, o.group);
11917                 delete schedules[i];
11918               }
11919             }
11920
11921             // Defer the first tick to end of the current frame; see d3/d3#1576.
11922             // Note the transition may be canceled after start and before the first tick!
11923             // Note this must be scheduled before the start event; see d3/d3-transition#16!
11924             // Assuming this is successful, subsequent callbacks go straight to tick.
11925             d3_timeout(function() {
11926               if (self.state === STARTED) {
11927                 self.state = RUNNING;
11928                 self.timer.restart(tick, self.delay, self.time);
11929                 tick(elapsed);
11930               }
11931             });
11932
11933             // Dispatch the start event.
11934             // Note this must be done before the tween are initialized.
11935             self.state = STARTING;
11936             self.on.call("start", node, node.__data__, self.index, self.group);
11937             if (self.state !== STARTING) { return; } // interrupted
11938             self.state = STARTED;
11939
11940             // Initialize the tween, deleting null tween.
11941             tween = new Array(n = self.tween.length);
11942             for (i = 0, j = -1; i < n; ++i) {
11943               if (o = self.tween[i].value.call(node, node.__data__, self.index, self.group)) {
11944                 tween[++j] = o;
11945               }
11946             }
11947             tween.length = j + 1;
11948           }
11949
11950           function tick(elapsed) {
11951             var t = elapsed < self.duration ? self.ease.call(null, elapsed / self.duration) : (self.timer.restart(stop), self.state = ENDING, 1),
11952                 i = -1,
11953                 n = tween.length;
11954
11955             while (++i < n) {
11956               tween[i].call(node, t);
11957             }
11958
11959             // Dispatch the end event.
11960             if (self.state === ENDING) {
11961               self.on.call("end", node, node.__data__, self.index, self.group);
11962               stop();
11963             }
11964           }
11965
11966           function stop() {
11967             self.state = ENDED;
11968             self.timer.stop();
11969             delete schedules[id];
11970             for (var i in schedules) { return; } // eslint-disable-line no-unused-vars
11971             delete node.__transition;
11972           }
11973         }
11974
11975         function interrupt(node, name) {
11976           var schedules = node.__transition,
11977               schedule,
11978               active,
11979               empty = true,
11980               i;
11981
11982           if (!schedules) { return; }
11983
11984           name = name == null ? null : name + "";
11985
11986           for (i in schedules) {
11987             if ((schedule = schedules[i]).name !== name) { empty = false; continue; }
11988             active = schedule.state > STARTING && schedule.state < ENDING;
11989             schedule.state = ENDED;
11990             schedule.timer.stop();
11991             schedule.on.call(active ? "interrupt" : "cancel", node, node.__data__, schedule.index, schedule.group);
11992             delete schedules[i];
11993           }
11994
11995           if (empty) { delete node.__transition; }
11996         }
11997
11998         function selection_interrupt(name) {
11999           return this.each(function() {
12000             interrupt(this, name);
12001           });
12002         }
12003
12004         function tweenRemove(id, name) {
12005           var tween0, tween1;
12006           return function() {
12007             var schedule = set$1(this, id),
12008                 tween = schedule.tween;
12009
12010             // If this node shared tween with the previous node,
12011             // just assign the updated shared tween and we’re done!
12012             // Otherwise, copy-on-write.
12013             if (tween !== tween0) {
12014               tween1 = tween0 = tween;
12015               for (var i = 0, n = tween1.length; i < n; ++i) {
12016                 if (tween1[i].name === name) {
12017                   tween1 = tween1.slice();
12018                   tween1.splice(i, 1);
12019                   break;
12020                 }
12021               }
12022             }
12023
12024             schedule.tween = tween1;
12025           };
12026         }
12027
12028         function tweenFunction(id, name, value) {
12029           var tween0, tween1;
12030           if (typeof value !== "function") { throw new Error; }
12031           return function() {
12032             var schedule = set$1(this, id),
12033                 tween = schedule.tween;
12034
12035             // If this node shared tween with the previous node,
12036             // just assign the updated shared tween and we’re done!
12037             // Otherwise, copy-on-write.
12038             if (tween !== tween0) {
12039               tween1 = (tween0 = tween).slice();
12040               for (var t = {name: name, value: value}, i = 0, n = tween1.length; i < n; ++i) {
12041                 if (tween1[i].name === name) {
12042                   tween1[i] = t;
12043                   break;
12044                 }
12045               }
12046               if (i === n) { tween1.push(t); }
12047             }
12048
12049             schedule.tween = tween1;
12050           };
12051         }
12052
12053         function transition_tween(name, value) {
12054           var id = this._id;
12055
12056           name += "";
12057
12058           if (arguments.length < 2) {
12059             var tween = get$2(this.node(), id).tween;
12060             for (var i = 0, n = tween.length, t; i < n; ++i) {
12061               if ((t = tween[i]).name === name) {
12062                 return t.value;
12063               }
12064             }
12065             return null;
12066           }
12067
12068           return this.each((value == null ? tweenRemove : tweenFunction)(id, name, value));
12069         }
12070
12071         function tweenValue(transition, name, value) {
12072           var id = transition._id;
12073
12074           transition.each(function() {
12075             var schedule = set$1(this, id);
12076             (schedule.value || (schedule.value = {}))[name] = value.apply(this, arguments);
12077           });
12078
12079           return function(node) {
12080             return get$2(node, id).value[name];
12081           };
12082         }
12083
12084         function interpolate$1(a, b) {
12085           var c;
12086           return (typeof b === "number" ? d3_interpolateNumber
12087               : b instanceof color ? d3_interpolateRgb
12088               : (c = color(b)) ? (b = c, d3_interpolateRgb)
12089               : interpolateString)(a, b);
12090         }
12091
12092         function attrRemove$1(name) {
12093           return function() {
12094             this.removeAttribute(name);
12095           };
12096         }
12097
12098         function attrRemoveNS$1(fullname) {
12099           return function() {
12100             this.removeAttributeNS(fullname.space, fullname.local);
12101           };
12102         }
12103
12104         function attrConstant$1(name, interpolate, value1) {
12105           var string00,
12106               string1 = value1 + "",
12107               interpolate0;
12108           return function() {
12109             var string0 = this.getAttribute(name);
12110             return string0 === string1 ? null
12111                 : string0 === string00 ? interpolate0
12112                 : interpolate0 = interpolate(string00 = string0, value1);
12113           };
12114         }
12115
12116         function attrConstantNS$1(fullname, interpolate, value1) {
12117           var string00,
12118               string1 = value1 + "",
12119               interpolate0;
12120           return function() {
12121             var string0 = this.getAttributeNS(fullname.space, fullname.local);
12122             return string0 === string1 ? null
12123                 : string0 === string00 ? interpolate0
12124                 : interpolate0 = interpolate(string00 = string0, value1);
12125           };
12126         }
12127
12128         function attrFunction$1(name, interpolate, value) {
12129           var string00,
12130               string10,
12131               interpolate0;
12132           return function() {
12133             var string0, value1 = value(this), string1;
12134             if (value1 == null) { return void this.removeAttribute(name); }
12135             string0 = this.getAttribute(name);
12136             string1 = value1 + "";
12137             return string0 === string1 ? null
12138                 : string0 === string00 && string1 === string10 ? interpolate0
12139                 : (string10 = string1, interpolate0 = interpolate(string00 = string0, value1));
12140           };
12141         }
12142
12143         function attrFunctionNS$1(fullname, interpolate, value) {
12144           var string00,
12145               string10,
12146               interpolate0;
12147           return function() {
12148             var string0, value1 = value(this), string1;
12149             if (value1 == null) { return void this.removeAttributeNS(fullname.space, fullname.local); }
12150             string0 = this.getAttributeNS(fullname.space, fullname.local);
12151             string1 = value1 + "";
12152             return string0 === string1 ? null
12153                 : string0 === string00 && string1 === string10 ? interpolate0
12154                 : (string10 = string1, interpolate0 = interpolate(string00 = string0, value1));
12155           };
12156         }
12157
12158         function transition_attr(name, value) {
12159           var fullname = namespace(name), i = fullname === "transform" ? interpolateTransformSvg : interpolate$1;
12160           return this.attrTween(name, typeof value === "function"
12161               ? (fullname.local ? attrFunctionNS$1 : attrFunction$1)(fullname, i, tweenValue(this, "attr." + name, value))
12162               : value == null ? (fullname.local ? attrRemoveNS$1 : attrRemove$1)(fullname)
12163               : (fullname.local ? attrConstantNS$1 : attrConstant$1)(fullname, i, value));
12164         }
12165
12166         function attrInterpolate(name, i) {
12167           return function(t) {
12168             this.setAttribute(name, i.call(this, t));
12169           };
12170         }
12171
12172         function attrInterpolateNS(fullname, i) {
12173           return function(t) {
12174             this.setAttributeNS(fullname.space, fullname.local, i.call(this, t));
12175           };
12176         }
12177
12178         function attrTweenNS(fullname, value) {
12179           var t0, i0;
12180           function tween() {
12181             var i = value.apply(this, arguments);
12182             if (i !== i0) { t0 = (i0 = i) && attrInterpolateNS(fullname, i); }
12183             return t0;
12184           }
12185           tween._value = value;
12186           return tween;
12187         }
12188
12189         function attrTween(name, value) {
12190           var t0, i0;
12191           function tween() {
12192             var i = value.apply(this, arguments);
12193             if (i !== i0) { t0 = (i0 = i) && attrInterpolate(name, i); }
12194             return t0;
12195           }
12196           tween._value = value;
12197           return tween;
12198         }
12199
12200         function transition_attrTween(name, value) {
12201           var key = "attr." + name;
12202           if (arguments.length < 2) { return (key = this.tween(key)) && key._value; }
12203           if (value == null) { return this.tween(key, null); }
12204           if (typeof value !== "function") { throw new Error; }
12205           var fullname = namespace(name);
12206           return this.tween(key, (fullname.local ? attrTweenNS : attrTween)(fullname, value));
12207         }
12208
12209         function delayFunction(id, value) {
12210           return function() {
12211             init(this, id).delay = +value.apply(this, arguments);
12212           };
12213         }
12214
12215         function delayConstant(id, value) {
12216           return value = +value, function() {
12217             init(this, id).delay = value;
12218           };
12219         }
12220
12221         function transition_delay(value) {
12222           var id = this._id;
12223
12224           return arguments.length
12225               ? this.each((typeof value === "function"
12226                   ? delayFunction
12227                   : delayConstant)(id, value))
12228               : get$2(this.node(), id).delay;
12229         }
12230
12231         function durationFunction(id, value) {
12232           return function() {
12233             set$1(this, id).duration = +value.apply(this, arguments);
12234           };
12235         }
12236
12237         function durationConstant(id, value) {
12238           return value = +value, function() {
12239             set$1(this, id).duration = value;
12240           };
12241         }
12242
12243         function transition_duration(value) {
12244           var id = this._id;
12245
12246           return arguments.length
12247               ? this.each((typeof value === "function"
12248                   ? durationFunction
12249                   : durationConstant)(id, value))
12250               : get$2(this.node(), id).duration;
12251         }
12252
12253         function easeConstant(id, value) {
12254           if (typeof value !== "function") { throw new Error; }
12255           return function() {
12256             set$1(this, id).ease = value;
12257           };
12258         }
12259
12260         function transition_ease(value) {
12261           var id = this._id;
12262
12263           return arguments.length
12264               ? this.each(easeConstant(id, value))
12265               : get$2(this.node(), id).ease;
12266         }
12267
12268         function transition_filter(match) {
12269           if (typeof match !== "function") { match = matcher(match); }
12270
12271           for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) {
12272             for (var group = groups[j], n = group.length, subgroup = subgroups[j] = [], node, i = 0; i < n; ++i) {
12273               if ((node = group[i]) && match.call(node, node.__data__, i, group)) {
12274                 subgroup.push(node);
12275               }
12276             }
12277           }
12278
12279           return new Transition(subgroups, this._parents, this._name, this._id);
12280         }
12281
12282         function transition_merge(transition) {
12283           if (transition._id !== this._id) { throw new Error; }
12284
12285           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) {
12286             for (var group0 = groups0[j], group1 = groups1[j], n = group0.length, merge = merges[j] = new Array(n), node, i = 0; i < n; ++i) {
12287               if (node = group0[i] || group1[i]) {
12288                 merge[i] = node;
12289               }
12290             }
12291           }
12292
12293           for (; j < m0; ++j) {
12294             merges[j] = groups0[j];
12295           }
12296
12297           return new Transition(merges, this._parents, this._name, this._id);
12298         }
12299
12300         function start(name) {
12301           return (name + "").trim().split(/^|\s+/).every(function(t) {
12302             var i = t.indexOf(".");
12303             if (i >= 0) { t = t.slice(0, i); }
12304             return !t || t === "start";
12305           });
12306         }
12307
12308         function onFunction(id, name, listener) {
12309           var on0, on1, sit = start(name) ? init : set$1;
12310           return function() {
12311             var schedule = sit(this, id),
12312                 on = schedule.on;
12313
12314             // If this node shared a dispatch with the previous node,
12315             // just assign the updated shared dispatch and we’re done!
12316             // Otherwise, copy-on-write.
12317             if (on !== on0) { (on1 = (on0 = on).copy()).on(name, listener); }
12318
12319             schedule.on = on1;
12320           };
12321         }
12322
12323         function transition_on(name, listener) {
12324           var id = this._id;
12325
12326           return arguments.length < 2
12327               ? get$2(this.node(), id).on.on(name)
12328               : this.each(onFunction(id, name, listener));
12329         }
12330
12331         function removeFunction(id) {
12332           return function() {
12333             var parent = this.parentNode;
12334             for (var i in this.__transition) { if (+i !== id) { return; } }
12335             if (parent) { parent.removeChild(this); }
12336           };
12337         }
12338
12339         function transition_remove() {
12340           return this.on("end.remove", removeFunction(this._id));
12341         }
12342
12343         function transition_select(select) {
12344           var name = this._name,
12345               id = this._id;
12346
12347           if (typeof select !== "function") { select = selector(select); }
12348
12349           for (var groups = this._groups, m = groups.length, subgroups = new Array(m), j = 0; j < m; ++j) {
12350             for (var group = groups[j], n = group.length, subgroup = subgroups[j] = new Array(n), node, subnode, i = 0; i < n; ++i) {
12351               if ((node = group[i]) && (subnode = select.call(node, node.__data__, i, group))) {
12352                 if ("__data__" in node) { subnode.__data__ = node.__data__; }
12353                 subgroup[i] = subnode;
12354                 schedule(subgroup[i], name, id, i, subgroup, get$2(node, id));
12355               }
12356             }
12357           }
12358
12359           return new Transition(subgroups, this._parents, name, id);
12360         }
12361
12362         function transition_selectAll(select) {
12363           var name = this._name,
12364               id = this._id;
12365
12366           if (typeof select !== "function") { select = selectorAll(select); }
12367
12368           for (var groups = this._groups, m = groups.length, subgroups = [], parents = [], j = 0; j < m; ++j) {
12369             for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) {
12370               if (node = group[i]) {
12371                 for (var children = select.call(node, node.__data__, i, group), child, inherit = get$2(node, id), k = 0, l = children.length; k < l; ++k) {
12372                   if (child = children[k]) {
12373                     schedule(child, name, id, k, children, inherit);
12374                   }
12375                 }
12376                 subgroups.push(children);
12377                 parents.push(node);
12378               }
12379             }
12380           }
12381
12382           return new Transition(subgroups, parents, name, id);
12383         }
12384
12385         var Selection$1 = selection.prototype.constructor;
12386
12387         function transition_selection() {
12388           return new Selection$1(this._groups, this._parents);
12389         }
12390
12391         function styleNull(name, interpolate) {
12392           var string00,
12393               string10,
12394               interpolate0;
12395           return function() {
12396             var string0 = styleValue(this, name),
12397                 string1 = (this.style.removeProperty(name), styleValue(this, name));
12398             return string0 === string1 ? null
12399                 : string0 === string00 && string1 === string10 ? interpolate0
12400                 : interpolate0 = interpolate(string00 = string0, string10 = string1);
12401           };
12402         }
12403
12404         function styleRemove$1(name) {
12405           return function() {
12406             this.style.removeProperty(name);
12407           };
12408         }
12409
12410         function styleConstant$1(name, interpolate, value1) {
12411           var string00,
12412               string1 = value1 + "",
12413               interpolate0;
12414           return function() {
12415             var string0 = styleValue(this, name);
12416             return string0 === string1 ? null
12417                 : string0 === string00 ? interpolate0
12418                 : interpolate0 = interpolate(string00 = string0, value1);
12419           };
12420         }
12421
12422         function styleFunction$1(name, interpolate, value) {
12423           var string00,
12424               string10,
12425               interpolate0;
12426           return function() {
12427             var string0 = styleValue(this, name),
12428                 value1 = value(this),
12429                 string1 = value1 + "";
12430             if (value1 == null) { string1 = value1 = (this.style.removeProperty(name), styleValue(this, name)); }
12431             return string0 === string1 ? null
12432                 : string0 === string00 && string1 === string10 ? interpolate0
12433                 : (string10 = string1, interpolate0 = interpolate(string00 = string0, value1));
12434           };
12435         }
12436
12437         function styleMaybeRemove(id, name) {
12438           var on0, on1, listener0, key = "style." + name, event = "end." + key, remove;
12439           return function() {
12440             var schedule = set$1(this, id),
12441                 on = schedule.on,
12442                 listener = schedule.value[key] == null ? remove || (remove = styleRemove$1(name)) : undefined;
12443
12444             // If this node shared a dispatch with the previous node,
12445             // just assign the updated shared dispatch and we’re done!
12446             // Otherwise, copy-on-write.
12447             if (on !== on0 || listener0 !== listener) { (on1 = (on0 = on).copy()).on(event, listener0 = listener); }
12448
12449             schedule.on = on1;
12450           };
12451         }
12452
12453         function transition_style(name, value, priority) {
12454           var i = (name += "") === "transform" ? interpolateTransformCss : interpolate$1;
12455           return value == null ? this
12456               .styleTween(name, styleNull(name, i))
12457               .on("end.style." + name, styleRemove$1(name))
12458             : typeof value === "function" ? this
12459               .styleTween(name, styleFunction$1(name, i, tweenValue(this, "style." + name, value)))
12460               .each(styleMaybeRemove(this._id, name))
12461             : this
12462               .styleTween(name, styleConstant$1(name, i, value), priority)
12463               .on("end.style." + name, null);
12464         }
12465
12466         function styleInterpolate(name, i, priority) {
12467           return function(t) {
12468             this.style.setProperty(name, i.call(this, t), priority);
12469           };
12470         }
12471
12472         function styleTween(name, value, priority) {
12473           var t, i0;
12474           function tween() {
12475             var i = value.apply(this, arguments);
12476             if (i !== i0) { t = (i0 = i) && styleInterpolate(name, i, priority); }
12477             return t;
12478           }
12479           tween._value = value;
12480           return tween;
12481         }
12482
12483         function transition_styleTween(name, value, priority) {
12484           var key = "style." + (name += "");
12485           if (arguments.length < 2) { return (key = this.tween(key)) && key._value; }
12486           if (value == null) { return this.tween(key, null); }
12487           if (typeof value !== "function") { throw new Error; }
12488           return this.tween(key, styleTween(name, value, priority == null ? "" : priority));
12489         }
12490
12491         function textConstant$1(value) {
12492           return function() {
12493             this.textContent = value;
12494           };
12495         }
12496
12497         function textFunction$1(value) {
12498           return function() {
12499             var value1 = value(this);
12500             this.textContent = value1 == null ? "" : value1;
12501           };
12502         }
12503
12504         function transition_text(value) {
12505           return this.tween("text", typeof value === "function"
12506               ? textFunction$1(tweenValue(this, "text", value))
12507               : textConstant$1(value == null ? "" : value + ""));
12508         }
12509
12510         function textInterpolate(i) {
12511           return function(t) {
12512             this.textContent = i.call(this, t);
12513           };
12514         }
12515
12516         function textTween(value) {
12517           var t0, i0;
12518           function tween() {
12519             var i = value.apply(this, arguments);
12520             if (i !== i0) { t0 = (i0 = i) && textInterpolate(i); }
12521             return t0;
12522           }
12523           tween._value = value;
12524           return tween;
12525         }
12526
12527         function transition_textTween(value) {
12528           var key = "text";
12529           if (arguments.length < 1) { return (key = this.tween(key)) && key._value; }
12530           if (value == null) { return this.tween(key, null); }
12531           if (typeof value !== "function") { throw new Error; }
12532           return this.tween(key, textTween(value));
12533         }
12534
12535         function transition_transition() {
12536           var name = this._name,
12537               id0 = this._id,
12538               id1 = newId();
12539
12540           for (var groups = this._groups, m = groups.length, j = 0; j < m; ++j) {
12541             for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) {
12542               if (node = group[i]) {
12543                 var inherit = get$2(node, id0);
12544                 schedule(node, name, id1, i, group, {
12545                   time: inherit.time + inherit.delay + inherit.duration,
12546                   delay: 0,
12547                   duration: inherit.duration,
12548                   ease: inherit.ease
12549                 });
12550               }
12551             }
12552           }
12553
12554           return new Transition(groups, this._parents, name, id1);
12555         }
12556
12557         function transition_end() {
12558           var on0, on1, that = this, id = that._id, size = that.size();
12559           return new Promise(function(resolve, reject) {
12560             var cancel = {value: reject},
12561                 end = {value: function() { if (--size === 0) { resolve(); } }};
12562
12563             that.each(function() {
12564               var schedule = set$1(this, id),
12565                   on = schedule.on;
12566
12567               // If this node shared a dispatch with the previous node,
12568               // just assign the updated shared dispatch and we’re done!
12569               // Otherwise, copy-on-write.
12570               if (on !== on0) {
12571                 on1 = (on0 = on).copy();
12572                 on1._.cancel.push(cancel);
12573                 on1._.interrupt.push(cancel);
12574                 on1._.end.push(end);
12575               }
12576
12577               schedule.on = on1;
12578             });
12579           });
12580         }
12581
12582         var id$3 = 0;
12583
12584         function Transition(groups, parents, name, id) {
12585           this._groups = groups;
12586           this._parents = parents;
12587           this._name = name;
12588           this._id = id;
12589         }
12590
12591         function transition(name) {
12592           return selection().transition(name);
12593         }
12594
12595         function newId() {
12596           return ++id$3;
12597         }
12598
12599         var selection_prototype = selection.prototype;
12600
12601         Transition.prototype = transition.prototype = {
12602           constructor: Transition,
12603           select: transition_select,
12604           selectAll: transition_selectAll,
12605           filter: transition_filter,
12606           merge: transition_merge,
12607           selection: transition_selection,
12608           transition: transition_transition,
12609           call: selection_prototype.call,
12610           nodes: selection_prototype.nodes,
12611           node: selection_prototype.node,
12612           size: selection_prototype.size,
12613           empty: selection_prototype.empty,
12614           each: selection_prototype.each,
12615           on: transition_on,
12616           attr: transition_attr,
12617           attrTween: transition_attrTween,
12618           style: transition_style,
12619           styleTween: transition_styleTween,
12620           text: transition_text,
12621           textTween: transition_textTween,
12622           remove: transition_remove,
12623           tween: transition_tween,
12624           delay: transition_delay,
12625           duration: transition_duration,
12626           ease: transition_ease,
12627           end: transition_end
12628         };
12629
12630         function linear$1(t) {
12631           return +t;
12632         }
12633
12634         function cubicInOut(t) {
12635           return ((t *= 2) <= 1 ? t * t * t : (t -= 2) * t * t + 2) / 2;
12636         }
12637
12638         var defaultTiming = {
12639           time: null, // Set on use.
12640           delay: 0,
12641           duration: 250,
12642           ease: cubicInOut
12643         };
12644
12645         function inherit(node, id) {
12646           var timing;
12647           while (!(timing = node.__transition) || !(timing = timing[id])) {
12648             if (!(node = node.parentNode)) {
12649               return defaultTiming.time = now(), defaultTiming;
12650             }
12651           }
12652           return timing;
12653         }
12654
12655         function selection_transition(name) {
12656           var id,
12657               timing;
12658
12659           if (name instanceof Transition) {
12660             id = name._id, name = name._name;
12661           } else {
12662             id = newId(), (timing = defaultTiming).time = now(), name = name == null ? null : name + "";
12663           }
12664
12665           for (var groups = this._groups, m = groups.length, j = 0; j < m; ++j) {
12666             for (var group = groups[j], n = group.length, node, i = 0; i < n; ++i) {
12667               if (node = group[i]) {
12668                 schedule(node, name, id, i, group, timing || inherit(node, id));
12669               }
12670             }
12671           }
12672
12673           return new Transition(groups, this._parents, name, id);
12674         }
12675
12676         selection.prototype.interrupt = selection_interrupt;
12677         selection.prototype.transition = selection_transition;
12678
12679         function constant$3(x) {
12680           return function() {
12681             return x;
12682           };
12683         }
12684
12685         function ZoomEvent(target, type, transform) {
12686           this.target = target;
12687           this.type = type;
12688           this.transform = transform;
12689         }
12690
12691         function Transform(k, x, y) {
12692           this.k = k;
12693           this.x = x;
12694           this.y = y;
12695         }
12696
12697         Transform.prototype = {
12698           constructor: Transform,
12699           scale: function(k) {
12700             return k === 1 ? this : new Transform(this.k * k, this.x, this.y);
12701           },
12702           translate: function(x, y) {
12703             return x === 0 & y === 0 ? this : new Transform(this.k, this.x + this.k * x, this.y + this.k * y);
12704           },
12705           apply: function(point) {
12706             return [point[0] * this.k + this.x, point[1] * this.k + this.y];
12707           },
12708           applyX: function(x) {
12709             return x * this.k + this.x;
12710           },
12711           applyY: function(y) {
12712             return y * this.k + this.y;
12713           },
12714           invert: function(location) {
12715             return [(location[0] - this.x) / this.k, (location[1] - this.y) / this.k];
12716           },
12717           invertX: function(x) {
12718             return (x - this.x) / this.k;
12719           },
12720           invertY: function(y) {
12721             return (y - this.y) / this.k;
12722           },
12723           rescaleX: function(x) {
12724             return x.copy().domain(x.range().map(this.invertX, this).map(x.invert, x));
12725           },
12726           rescaleY: function(y) {
12727             return y.copy().domain(y.range().map(this.invertY, this).map(y.invert, y));
12728           },
12729           toString: function() {
12730             return "translate(" + this.x + "," + this.y + ") scale(" + this.k + ")";
12731           }
12732         };
12733
12734         var identity$2 = new Transform(1, 0, 0);
12735
12736         function nopropagation$1() {
12737           event.stopImmediatePropagation();
12738         }
12739
12740         function noevent$1() {
12741           event.preventDefault();
12742           event.stopImmediatePropagation();
12743         }
12744
12745         // Ignore right-click, since that should open the context menu.
12746         function defaultFilter$1() {
12747           return !event.ctrlKey && !event.button;
12748         }
12749
12750         function defaultExtent() {
12751           var e = this;
12752           if (e instanceof SVGElement) {
12753             e = e.ownerSVGElement || e;
12754             if (e.hasAttribute("viewBox")) {
12755               e = e.viewBox.baseVal;
12756               return [[e.x, e.y], [e.x + e.width, e.y + e.height]];
12757             }
12758             return [[0, 0], [e.width.baseVal.value, e.height.baseVal.value]];
12759           }
12760           return [[0, 0], [e.clientWidth, e.clientHeight]];
12761         }
12762
12763         function defaultTransform() {
12764           return this.__zoom || identity$2;
12765         }
12766
12767         function defaultWheelDelta() {
12768           return -event.deltaY * (event.deltaMode === 1 ? 0.05 : event.deltaMode ? 1 : 0.002);
12769         }
12770
12771         function defaultTouchable$1() {
12772           return navigator.maxTouchPoints || ("ontouchstart" in this);
12773         }
12774
12775         function defaultConstrain(transform, extent, translateExtent) {
12776           var dx0 = transform.invertX(extent[0][0]) - translateExtent[0][0],
12777               dx1 = transform.invertX(extent[1][0]) - translateExtent[1][0],
12778               dy0 = transform.invertY(extent[0][1]) - translateExtent[0][1],
12779               dy1 = transform.invertY(extent[1][1]) - translateExtent[1][1];
12780           return transform.translate(
12781             dx1 > dx0 ? (dx0 + dx1) / 2 : Math.min(0, dx0) || Math.max(0, dx1),
12782             dy1 > dy0 ? (dy0 + dy1) / 2 : Math.min(0, dy0) || Math.max(0, dy1)
12783           );
12784         }
12785
12786         function d3_zoom() {
12787           var filter = defaultFilter$1,
12788               extent = defaultExtent,
12789               constrain = defaultConstrain,
12790               wheelDelta = defaultWheelDelta,
12791               touchable = defaultTouchable$1,
12792               scaleExtent = [0, Infinity],
12793               translateExtent = [[-Infinity, -Infinity], [Infinity, Infinity]],
12794               duration = 250,
12795               interpolate = interpolateZoom,
12796               listeners = dispatch("start", "zoom", "end"),
12797               touchstarting,
12798               touchending,
12799               touchDelay = 500,
12800               wheelDelay = 150,
12801               clickDistance2 = 0;
12802
12803           function zoom(selection) {
12804             selection
12805                 .property("__zoom", defaultTransform)
12806                 .on("wheel.zoom", wheeled)
12807                 .on("mousedown.zoom", mousedowned)
12808                 .on("dblclick.zoom", dblclicked)
12809               .filter(touchable)
12810                 .on("touchstart.zoom", touchstarted)
12811                 .on("touchmove.zoom", touchmoved)
12812                 .on("touchend.zoom touchcancel.zoom", touchended)
12813                 .style("touch-action", "none")
12814                 .style("-webkit-tap-highlight-color", "rgba(0,0,0,0)");
12815           }
12816
12817           zoom.transform = function(collection, transform, point) {
12818             var selection = collection.selection ? collection.selection() : collection;
12819             selection.property("__zoom", defaultTransform);
12820             if (collection !== selection) {
12821               schedule(collection, transform, point);
12822             } else {
12823               selection.interrupt().each(function() {
12824                 gesture(this, arguments)
12825                     .start()
12826                     .zoom(null, typeof transform === "function" ? transform.apply(this, arguments) : transform)
12827                     .end();
12828               });
12829             }
12830           };
12831
12832           zoom.scaleBy = function(selection, k, p) {
12833             zoom.scaleTo(selection, function() {
12834               var k0 = this.__zoom.k,
12835                   k1 = typeof k === "function" ? k.apply(this, arguments) : k;
12836               return k0 * k1;
12837             }, p);
12838           };
12839
12840           zoom.scaleTo = function(selection, k, p) {
12841             zoom.transform(selection, function() {
12842               var e = extent.apply(this, arguments),
12843                   t0 = this.__zoom,
12844                   p0 = p == null ? centroid(e) : typeof p === "function" ? p.apply(this, arguments) : p,
12845                   p1 = t0.invert(p0),
12846                   k1 = typeof k === "function" ? k.apply(this, arguments) : k;
12847               return constrain(translate(scale(t0, k1), p0, p1), e, translateExtent);
12848             }, p);
12849           };
12850
12851           zoom.translateBy = function(selection, x, y) {
12852             zoom.transform(selection, function() {
12853               return constrain(this.__zoom.translate(
12854                 typeof x === "function" ? x.apply(this, arguments) : x,
12855                 typeof y === "function" ? y.apply(this, arguments) : y
12856               ), extent.apply(this, arguments), translateExtent);
12857             });
12858           };
12859
12860           zoom.translateTo = function(selection, x, y, p) {
12861             zoom.transform(selection, function() {
12862               var e = extent.apply(this, arguments),
12863                   t = this.__zoom,
12864                   p0 = p == null ? centroid(e) : typeof p === "function" ? p.apply(this, arguments) : p;
12865               return constrain(identity$2.translate(p0[0], p0[1]).scale(t.k).translate(
12866                 typeof x === "function" ? -x.apply(this, arguments) : -x,
12867                 typeof y === "function" ? -y.apply(this, arguments) : -y
12868               ), e, translateExtent);
12869             }, p);
12870           };
12871
12872           function scale(transform, k) {
12873             k = Math.max(scaleExtent[0], Math.min(scaleExtent[1], k));
12874             return k === transform.k ? transform : new Transform(k, transform.x, transform.y);
12875           }
12876
12877           function translate(transform, p0, p1) {
12878             var x = p0[0] - p1[0] * transform.k, y = p0[1] - p1[1] * transform.k;
12879             return x === transform.x && y === transform.y ? transform : new Transform(transform.k, x, y);
12880           }
12881
12882           function centroid(extent) {
12883             return [(+extent[0][0] + +extent[1][0]) / 2, (+extent[0][1] + +extent[1][1]) / 2];
12884           }
12885
12886           function schedule(transition, transform, point) {
12887             transition
12888                 .on("start.zoom", function() { gesture(this, arguments).start(); })
12889                 .on("interrupt.zoom end.zoom", function() { gesture(this, arguments).end(); })
12890                 .tween("zoom", function() {
12891                   var that = this,
12892                       args = arguments,
12893                       g = gesture(that, args),
12894                       e = extent.apply(that, args),
12895                       p = point == null ? centroid(e) : typeof point === "function" ? point.apply(that, args) : point,
12896                       w = Math.max(e[1][0] - e[0][0], e[1][1] - e[0][1]),
12897                       a = that.__zoom,
12898                       b = typeof transform === "function" ? transform.apply(that, args) : transform,
12899                       i = interpolate(a.invert(p).concat(w / a.k), b.invert(p).concat(w / b.k));
12900                   return function(t) {
12901                     if (t === 1) { t = b; } // Avoid rounding error on end.
12902                     else { var l = i(t), k = w / l[2]; t = new Transform(k, p[0] - l[0] * k, p[1] - l[1] * k); }
12903                     g.zoom(null, t);
12904                   };
12905                 });
12906           }
12907
12908           function gesture(that, args, clean) {
12909             return (!clean && that.__zooming) || new Gesture(that, args);
12910           }
12911
12912           function Gesture(that, args) {
12913             this.that = that;
12914             this.args = args;
12915             this.active = 0;
12916             this.extent = extent.apply(that, args);
12917             this.taps = 0;
12918           }
12919
12920           Gesture.prototype = {
12921             start: function() {
12922               if (++this.active === 1) {
12923                 this.that.__zooming = this;
12924                 this.emit("start");
12925               }
12926               return this;
12927             },
12928             zoom: function(key, transform) {
12929               if (this.mouse && key !== "mouse") { this.mouse[1] = transform.invert(this.mouse[0]); }
12930               if (this.touch0 && key !== "touch") { this.touch0[1] = transform.invert(this.touch0[0]); }
12931               if (this.touch1 && key !== "touch") { this.touch1[1] = transform.invert(this.touch1[0]); }
12932               this.that.__zoom = transform;
12933               this.emit("zoom");
12934               return this;
12935             },
12936             end: function() {
12937               if (--this.active === 0) {
12938                 delete this.that.__zooming;
12939                 this.emit("end");
12940               }
12941               return this;
12942             },
12943             emit: function(type) {
12944               customEvent(new ZoomEvent(zoom, type, this.that.__zoom), listeners.apply, listeners, [type, this.that, this.args]);
12945             }
12946           };
12947
12948           function wheeled() {
12949             if (!filter.apply(this, arguments)) { return; }
12950             var g = gesture(this, arguments),
12951                 t = this.__zoom,
12952                 k = Math.max(scaleExtent[0], Math.min(scaleExtent[1], t.k * Math.pow(2, wheelDelta.apply(this, arguments)))),
12953                 p = mouse(this);
12954
12955             // If the mouse is in the same location as before, reuse it.
12956             // If there were recent wheel events, reset the wheel idle timeout.
12957             if (g.wheel) {
12958               if (g.mouse[0][0] !== p[0] || g.mouse[0][1] !== p[1]) {
12959                 g.mouse[1] = t.invert(g.mouse[0] = p);
12960               }
12961               clearTimeout(g.wheel);
12962             }
12963
12964             // If this wheel event won’t trigger a transform change, ignore it.
12965             else if (t.k === k) { return; }
12966
12967             // Otherwise, capture the mouse point and location at the start.
12968             else {
12969               g.mouse = [p, t.invert(p)];
12970               interrupt(this);
12971               g.start();
12972             }
12973
12974             noevent$1();
12975             g.wheel = setTimeout(wheelidled, wheelDelay);
12976             g.zoom("mouse", constrain(translate(scale(t, k), g.mouse[0], g.mouse[1]), g.extent, translateExtent));
12977
12978             function wheelidled() {
12979               g.wheel = null;
12980               g.end();
12981             }
12982           }
12983
12984           function mousedowned() {
12985             if (touchending || !filter.apply(this, arguments)) { return; }
12986             var g = gesture(this, arguments, true),
12987                 v = select(event.view).on("mousemove.zoom", mousemoved, true).on("mouseup.zoom", mouseupped, true),
12988                 p = mouse(this),
12989                 x0 = event.clientX,
12990                 y0 = event.clientY;
12991
12992             dragDisable(event.view);
12993             nopropagation$1();
12994             g.mouse = [p, this.__zoom.invert(p)];
12995             interrupt(this);
12996             g.start();
12997
12998             function mousemoved() {
12999               noevent$1();
13000               if (!g.moved) {
13001                 var dx = event.clientX - x0, dy = event.clientY - y0;
13002                 g.moved = dx * dx + dy * dy > clickDistance2;
13003               }
13004               g.zoom("mouse", constrain(translate(g.that.__zoom, g.mouse[0] = mouse(g.that), g.mouse[1]), g.extent, translateExtent));
13005             }
13006
13007             function mouseupped() {
13008               v.on("mousemove.zoom mouseup.zoom", null);
13009               yesdrag(event.view, g.moved);
13010               noevent$1();
13011               g.end();
13012             }
13013           }
13014
13015           function dblclicked() {
13016             if (!filter.apply(this, arguments)) { return; }
13017             var t0 = this.__zoom,
13018                 p0 = mouse(this),
13019                 p1 = t0.invert(p0),
13020                 k1 = t0.k * (event.shiftKey ? 0.5 : 2),
13021                 t1 = constrain(translate(scale(t0, k1), p0, p1), extent.apply(this, arguments), translateExtent);
13022
13023             noevent$1();
13024             if (duration > 0) { select(this).transition().duration(duration).call(schedule, t1, p0); }
13025             else { select(this).call(zoom.transform, t1); }
13026           }
13027
13028           function touchstarted() {
13029             if (!filter.apply(this, arguments)) { return; }
13030             var touches = event.touches,
13031                 n = touches.length,
13032                 g = gesture(this, arguments, event.changedTouches.length === n),
13033                 started, i, t, p;
13034
13035             nopropagation$1();
13036             for (i = 0; i < n; ++i) {
13037               t = touches[i], p = touch(this, touches, t.identifier);
13038               p = [p, this.__zoom.invert(p), t.identifier];
13039               if (!g.touch0) { g.touch0 = p, started = true, g.taps = 1 + !!touchstarting; }
13040               else if (!g.touch1 && g.touch0[2] !== p[2]) { g.touch1 = p, g.taps = 0; }
13041             }
13042
13043             if (touchstarting) { touchstarting = clearTimeout(touchstarting); }
13044
13045             if (started) {
13046               if (g.taps < 2) { touchstarting = setTimeout(function() { touchstarting = null; }, touchDelay); }
13047               interrupt(this);
13048               g.start();
13049             }
13050           }
13051
13052           function touchmoved() {
13053             if (!this.__zooming) { return; }
13054             var g = gesture(this, arguments),
13055                 touches = event.changedTouches,
13056                 n = touches.length, i, t, p, l;
13057
13058             noevent$1();
13059             if (touchstarting) { touchstarting = clearTimeout(touchstarting); }
13060             g.taps = 0;
13061             for (i = 0; i < n; ++i) {
13062               t = touches[i], p = touch(this, touches, t.identifier);
13063               if (g.touch0 && g.touch0[2] === t.identifier) { g.touch0[0] = p; }
13064               else if (g.touch1 && g.touch1[2] === t.identifier) { g.touch1[0] = p; }
13065             }
13066             t = g.that.__zoom;
13067             if (g.touch1) {
13068               var p0 = g.touch0[0], l0 = g.touch0[1],
13069                   p1 = g.touch1[0], l1 = g.touch1[1],
13070                   dp = (dp = p1[0] - p0[0]) * dp + (dp = p1[1] - p0[1]) * dp,
13071                   dl = (dl = l1[0] - l0[0]) * dl + (dl = l1[1] - l0[1]) * dl;
13072               t = scale(t, Math.sqrt(dp / dl));
13073               p = [(p0[0] + p1[0]) / 2, (p0[1] + p1[1]) / 2];
13074               l = [(l0[0] + l1[0]) / 2, (l0[1] + l1[1]) / 2];
13075             }
13076             else if (g.touch0) { p = g.touch0[0], l = g.touch0[1]; }
13077             else { return; }
13078             g.zoom("touch", constrain(translate(t, p, l), g.extent, translateExtent));
13079           }
13080
13081           function touchended() {
13082             if (!this.__zooming) { return; }
13083             var g = gesture(this, arguments),
13084                 touches = event.changedTouches,
13085                 n = touches.length, i, t;
13086
13087             nopropagation$1();
13088             if (touchending) { clearTimeout(touchending); }
13089             touchending = setTimeout(function() { touchending = null; }, touchDelay);
13090             for (i = 0; i < n; ++i) {
13091               t = touches[i];
13092               if (g.touch0 && g.touch0[2] === t.identifier) { delete g.touch0; }
13093               else if (g.touch1 && g.touch1[2] === t.identifier) { delete g.touch1; }
13094             }
13095             if (g.touch1 && !g.touch0) { g.touch0 = g.touch1, delete g.touch1; }
13096             if (g.touch0) { g.touch0[1] = this.__zoom.invert(g.touch0[0]); }
13097             else {
13098               g.end();
13099               // If this was a dbltap, reroute to the (optional) dblclick.zoom handler.
13100               if (g.taps === 2) {
13101                 var p = select(this).on("dblclick.zoom");
13102                 if (p) { p.apply(this, arguments); }
13103               }
13104             }
13105           }
13106
13107           zoom.wheelDelta = function(_) {
13108             return arguments.length ? (wheelDelta = typeof _ === "function" ? _ : constant$3(+_), zoom) : wheelDelta;
13109           };
13110
13111           zoom.filter = function(_) {
13112             return arguments.length ? (filter = typeof _ === "function" ? _ : constant$3(!!_), zoom) : filter;
13113           };
13114
13115           zoom.touchable = function(_) {
13116             return arguments.length ? (touchable = typeof _ === "function" ? _ : constant$3(!!_), zoom) : touchable;
13117           };
13118
13119           zoom.extent = function(_) {
13120             return arguments.length ? (extent = typeof _ === "function" ? _ : constant$3([[+_[0][0], +_[0][1]], [+_[1][0], +_[1][1]]]), zoom) : extent;
13121           };
13122
13123           zoom.scaleExtent = function(_) {
13124             return arguments.length ? (scaleExtent[0] = +_[0], scaleExtent[1] = +_[1], zoom) : [scaleExtent[0], scaleExtent[1]];
13125           };
13126
13127           zoom.translateExtent = function(_) {
13128             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]]];
13129           };
13130
13131           zoom.constrain = function(_) {
13132             return arguments.length ? (constrain = _, zoom) : constrain;
13133           };
13134
13135           zoom.duration = function(_) {
13136             return arguments.length ? (duration = +_, zoom) : duration;
13137           };
13138
13139           zoom.interpolate = function(_) {
13140             return arguments.length ? (interpolate = _, zoom) : interpolate;
13141           };
13142
13143           zoom.on = function() {
13144             var value = listeners.on.apply(listeners, arguments);
13145             return value === listeners ? zoom : value;
13146           };
13147
13148           zoom.clickDistance = function(_) {
13149             return arguments.length ? (clickDistance2 = (_ = +_) * _, zoom) : Math.sqrt(clickDistance2);
13150           };
13151
13152           return zoom;
13153         }
13154
13155         /*
13156             Bypasses features of D3's default projection stream pipeline that are unnecessary:
13157             * Antimeridian clipping
13158             * Spherical rotation
13159             * Resampling
13160         */
13161         function geoRawMercator() {
13162             var project = mercatorRaw;
13163             var k = 512 / Math.PI; // scale
13164             var x = 0;
13165             var y = 0; // translate
13166             var clipExtent = [[0, 0], [0, 0]];
13167
13168
13169             function projection(point) {
13170                 point = project(point[0] * Math.PI / 180, point[1] * Math.PI / 180);
13171                 return [point[0] * k + x, y - point[1] * k];
13172             }
13173
13174
13175             projection.invert = function(point) {
13176                 point = project.invert((point[0] - x) / k, (y - point[1]) / k);
13177                 return point && [point[0] * 180 / Math.PI, point[1] * 180 / Math.PI];
13178             };
13179
13180
13181             projection.scale = function(_) {
13182                 if (!arguments.length) { return k; }
13183                 k = +_;
13184                 return projection;
13185             };
13186
13187
13188             projection.translate = function(_) {
13189                 if (!arguments.length) { return [x, y]; }
13190                 x = +_[0];
13191                 y = +_[1];
13192                 return projection;
13193             };
13194
13195
13196             projection.clipExtent = function(_) {
13197                 if (!arguments.length) { return clipExtent; }
13198                 clipExtent = _;
13199                 return projection;
13200             };
13201
13202
13203             projection.transform = function(obj) {
13204                 if (!arguments.length) { return identity$2.translate(x, y).scale(k); }
13205                 x = +obj.x;
13206                 y = +obj.y;
13207                 k = +obj.k;
13208                 return projection;
13209             };
13210
13211
13212             projection.stream = d3_geoTransform({
13213                 point: function(x, y) {
13214                     var vec = projection([x, y]);
13215                     this.stream.point(vec[0], vec[1]);
13216                 }
13217             }).stream;
13218
13219
13220             return projection;
13221         }
13222
13223         function geoOrthoNormalizedDotProduct(a, b, origin) {
13224             if (geoVecEqual(origin, a) || geoVecEqual(origin, b)) {
13225                 return 1;  // coincident points, treat as straight and try to remove
13226             }
13227             return geoVecNormalizedDot(a, b, origin);
13228         }
13229
13230
13231         function geoOrthoFilterDotProduct(dotp, epsilon, lowerThreshold, upperThreshold, allowStraightAngles) {
13232             var val = Math.abs(dotp);
13233             if (val < epsilon) {
13234                 return 0;      // already orthogonal
13235             } else if (allowStraightAngles && Math.abs(val-1) < epsilon) {
13236                 return 0;      // straight angle, which is okay in this case
13237             } else if (val < lowerThreshold || val > upperThreshold) {
13238                 return dotp;   // can be adjusted
13239             } else {
13240                 return null;   // ignore vertex
13241             }
13242         }
13243
13244
13245         function geoOrthoCalcScore(points, isClosed, epsilon, threshold) {
13246             var score = 0;
13247             var first = isClosed ? 0 : 1;
13248             var last = isClosed ? points.length : points.length - 1;
13249             var coords = points.map(function(p) { return p.coord; });
13250
13251             var lowerThreshold = Math.cos((90 - threshold) * Math.PI / 180);
13252             var upperThreshold = Math.cos(threshold * Math.PI / 180);
13253
13254             for (var i = first; i < last; i++) {
13255                 var a = coords[(i - 1 + coords.length) % coords.length];
13256                 var origin = coords[i];
13257                 var b = coords[(i + 1) % coords.length];
13258
13259                 var dotp = geoOrthoFilterDotProduct(geoOrthoNormalizedDotProduct(a, b, origin), epsilon, lowerThreshold, upperThreshold);
13260                 if (dotp === null) { continue; }    // ignore vertex
13261                 score = score + 2.0 * Math.min(Math.abs(dotp - 1.0), Math.min(Math.abs(dotp), Math.abs(dotp + 1)));
13262             }
13263
13264             return score;
13265         }
13266
13267         // returns the maximum angle less than `lessThan` between the actual corner and a 0° or 90° corner
13268         function geoOrthoMaxOffsetAngle(coords, isClosed, lessThan) {
13269             var max = -Infinity;
13270
13271             var first = isClosed ? 0 : 1;
13272             var last = isClosed ? coords.length : coords.length - 1;
13273
13274             for (var i = first; i < last; i++) {
13275                 var a = coords[(i - 1 + coords.length) % coords.length];
13276                 var origin = coords[i];
13277                 var b = coords[(i + 1) % coords.length];
13278                 var normalizedDotP = geoOrthoNormalizedDotProduct(a, b, origin);
13279
13280                 var angle = Math.acos(Math.abs(normalizedDotP)) * 180 / Math.PI;
13281
13282                 if (angle > 45) { angle = 90 - angle; }
13283
13284                 if (angle >= lessThan) { continue; }
13285
13286                 if (angle > max) { max = angle; }
13287             }
13288
13289             if (max === -Infinity) { return null; }
13290
13291             return max;
13292         }
13293
13294
13295         // similar to geoOrthoCalcScore, but returns quickly if there is something to do
13296         function geoOrthoCanOrthogonalize(coords, isClosed, epsilon, threshold, allowStraightAngles) {
13297             var score = null;
13298             var first = isClosed ? 0 : 1;
13299             var last = isClosed ? coords.length : coords.length - 1;
13300
13301             var lowerThreshold = Math.cos((90 - threshold) * Math.PI / 180);
13302             var upperThreshold = Math.cos(threshold * Math.PI / 180);
13303
13304             for (var i = first; i < last; i++) {
13305                 var a = coords[(i - 1 + coords.length) % coords.length];
13306                 var origin = coords[i];
13307                 var b = coords[(i + 1) % coords.length];
13308
13309                 var dotp = geoOrthoFilterDotProduct(geoOrthoNormalizedDotProduct(a, b, origin), epsilon, lowerThreshold, upperThreshold, allowStraightAngles);
13310                 if (dotp === null) { continue; }        // ignore vertex
13311                 if (Math.abs(dotp) > 0) { return 1; }   // something to do
13312                 score = 0;                          // already square
13313             }
13314
13315             return score;
13316         }
13317
13318         // Returns true if a and b have the same elements at the same indices.
13319         function utilArrayIdentical(a, b) {
13320             // an array is always identical to itself
13321             if (a === b) { return true; }
13322
13323             var i = a.length;
13324             if (i !== b.length) { return false; }
13325             while (i--) {
13326                 if (a[i] !== b[i]) { return false; }
13327             }
13328             return true;
13329         }
13330
13331         // http://2ality.com/2015/01/es6-set-operations.html
13332
13333         // Difference (a \ b): create a set that contains those elements of set a that are not in set b.
13334         // This operation is also sometimes called minus (-).
13335         // var a = [1,2,3];
13336         // var b = [4,3,2];
13337         // utilArrayDifference(a, b)
13338         //   [1]
13339         // utilArrayDifference(b, a)
13340         //   [4]
13341         function utilArrayDifference(a, b) {
13342             var other = new Set(b);
13343             return Array.from(new Set(a))
13344                 .filter(function(v) { return !other.has(v); });
13345         }
13346
13347         // Intersection (a ∩ b): create a set that contains those elements of set a that are also in set b.
13348         // var a = [1,2,3];
13349         // var b = [4,3,2];
13350         // utilArrayIntersection(a, b)
13351         //   [2,3]
13352         function utilArrayIntersection(a, b) {
13353             var other = new Set(b);
13354             return Array.from(new Set(a))
13355                 .filter(function(v) { return other.has(v); });
13356         }
13357
13358         // Union (a ∪ b): create a set that contains the elements of both set a and set b.
13359         // var a = [1,2,3];
13360         // var b = [4,3,2];
13361         // utilArrayUnion(a, b)
13362         //   [1,2,3,4]
13363         function utilArrayUnion(a, b) {
13364             var result = new Set(a);
13365             b.forEach(function(v) { result.add(v); });
13366             return Array.from(result);
13367         }
13368
13369         // Returns an Array with all the duplicates removed
13370         // var a = [1,1,2,3,3];
13371         // utilArrayUniq(a)
13372         //   [1,2,3]
13373         function utilArrayUniq(a) {
13374             return Array.from(new Set(a));
13375         }
13376
13377
13378         // Splits array into chunks of given chunk size
13379         // var a = [1,2,3,4,5,6,7];
13380         // utilArrayChunk(a, 3);
13381         //   [[1,2,3],[4,5,6],[7]];
13382         function utilArrayChunk(a, chunkSize) {
13383             if (!chunkSize || chunkSize < 0) { return [a.slice()]; }
13384
13385             var result = new Array(Math.ceil(a.length / chunkSize));
13386             return Array.from(result, function(item, i) {
13387                 return a.slice(i * chunkSize, i * chunkSize + chunkSize);
13388             });
13389         }
13390
13391
13392         // Flattens two level array into a single level
13393         // var a = [[1,2,3],[4,5,6],[7]];
13394         // utilArrayFlatten(a);
13395         //   [1,2,3,4,5,6,7];
13396         function utilArrayFlatten(a) {
13397             return a.reduce(function(acc, val) {
13398                 return acc.concat(val);
13399             }, []);
13400         }
13401
13402
13403         // Groups the items of the Array according to the given key
13404         // `key` can be passed as a property or as a key function
13405         //
13406         // var pets = [
13407         //     { type: 'Dog', name: 'Spot' },
13408         //     { type: 'Cat', name: 'Tiger' },
13409         //     { type: 'Dog', name: 'Rover' },
13410         //     { type: 'Cat', name: 'Leo' }
13411         // ];
13412         //
13413         // utilArrayGroupBy(pets, 'type')
13414         //   {
13415         //     'Dog': [{type: 'Dog', name: 'Spot'}, {type: 'Dog', name: 'Rover'}],
13416         //     'Cat': [{type: 'Cat', name: 'Tiger'}, {type: 'Cat', name: 'Leo'}]
13417         //   }
13418         //
13419         // utilArrayGroupBy(pets, function(item) { return item.name.length; })
13420         //   {
13421         //     3: [{type: 'Cat', name: 'Leo'}],
13422         //     4: [{type: 'Dog', name: 'Spot'}],
13423         //     5: [{type: 'Cat', name: 'Tiger'}, {type: 'Dog', name: 'Rover'}]
13424         //   }
13425         function utilArrayGroupBy(a, key) {
13426             return a.reduce(function(acc, item) {
13427                 var group = (typeof key === 'function') ? key(item) : item[key];
13428                 (acc[group] = acc[group] || []).push(item);
13429                 return acc;
13430             }, {});
13431         }
13432
13433
13434         // Returns an Array with all the duplicates removed
13435         // where uniqueness determined by the given key
13436         // `key` can be passed as a property or as a key function
13437         //
13438         // var pets = [
13439         //     { type: 'Dog', name: 'Spot' },
13440         //     { type: 'Cat', name: 'Tiger' },
13441         //     { type: 'Dog', name: 'Rover' },
13442         //     { type: 'Cat', name: 'Leo' }
13443         // ];
13444         //
13445         // utilArrayUniqBy(pets, 'type')
13446         //   [
13447         //     { type: 'Dog', name: 'Spot' },
13448         //     { type: 'Cat', name: 'Tiger' }
13449         //   ]
13450         //
13451         // utilArrayUniqBy(pets, function(item) { return item.name.length; })
13452         //   [
13453         //     { type: 'Dog', name: 'Spot' },
13454         //     { type: 'Cat', name: 'Tiger' },
13455         //     { type: 'Cat', name: 'Leo' }
13456         //   }
13457         function utilArrayUniqBy(a, key) {
13458             var seen = new Set();
13459             return a.reduce(function(acc, item) {
13460                 var val = (typeof key === 'function') ? key(item) : item[key];
13461                 if (val && !seen.has(val)) {
13462                     seen.add(val);
13463                     acc.push(item);
13464                 }
13465                 return acc;
13466             }, []);
13467         }
13468
13469         var remove$1 = removeDiacritics;
13470
13471         var replacementList = [
13472           {
13473             base: ' ',
13474             chars: "\u00A0",
13475           }, {
13476             base: '0',
13477             chars: "\u07C0",
13478           }, {
13479             base: 'A',
13480             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",
13481           }, {
13482             base: 'AA',
13483             chars: "\uA732",
13484           }, {
13485             base: 'AE',
13486             chars: "\u00C6\u01FC\u01E2",
13487           }, {
13488             base: 'AO',
13489             chars: "\uA734",
13490           }, {
13491             base: 'AU',
13492             chars: "\uA736",
13493           }, {
13494             base: 'AV',
13495             chars: "\uA738\uA73A",
13496           }, {
13497             base: 'AY',
13498             chars: "\uA73C",
13499           }, {
13500             base: 'B',
13501             chars: "\u24B7\uFF22\u1E02\u1E04\u1E06\u0243\u0181",
13502           }, {
13503             base: 'C',
13504             chars: "\u24b8\uff23\uA73E\u1E08\u0106\u0043\u0108\u010A\u010C\u00C7\u0187\u023B",
13505           }, {
13506             base: 'D',
13507             chars: "\u24B9\uFF24\u1E0A\u010E\u1E0C\u1E10\u1E12\u1E0E\u0110\u018A\u0189\u1D05\uA779",
13508           }, {
13509             base: 'Dh',
13510             chars: "\u00D0",
13511           }, {
13512             base: 'DZ',
13513             chars: "\u01F1\u01C4",
13514           }, {
13515             base: 'Dz',
13516             chars: "\u01F2\u01C5",
13517           }, {
13518             base: 'E',
13519             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",
13520           }, {
13521             base: 'F',
13522             chars: "\uA77C\u24BB\uFF26\u1E1E\u0191\uA77B",
13523           }, {
13524             base: 'G',
13525             chars: "\u24BC\uFF27\u01F4\u011C\u1E20\u011E\u0120\u01E6\u0122\u01E4\u0193\uA7A0\uA77D\uA77E\u0262",
13526           }, {
13527             base: 'H',
13528             chars: "\u24BD\uFF28\u0124\u1E22\u1E26\u021E\u1E24\u1E28\u1E2A\u0126\u2C67\u2C75\uA78D",
13529           }, {
13530             base: 'I',
13531             chars: "\u24BE\uFF29\xCC\xCD\xCE\u0128\u012A\u012C\u0130\xCF\u1E2E\u1EC8\u01CF\u0208\u020A\u1ECA\u012E\u1E2C\u0197",
13532           }, {
13533             base: 'J',
13534             chars: "\u24BF\uFF2A\u0134\u0248\u0237",
13535           }, {
13536             base: 'K',
13537             chars: "\u24C0\uFF2B\u1E30\u01E8\u1E32\u0136\u1E34\u0198\u2C69\uA740\uA742\uA744\uA7A2",
13538           }, {
13539             base: 'L',
13540             chars: "\u24C1\uFF2C\u013F\u0139\u013D\u1E36\u1E38\u013B\u1E3C\u1E3A\u0141\u023D\u2C62\u2C60\uA748\uA746\uA780",
13541           }, {
13542             base: 'LJ',
13543             chars: "\u01C7",
13544           }, {
13545             base: 'Lj',
13546             chars: "\u01C8",
13547           }, {
13548             base: 'M',
13549             chars: "\u24C2\uFF2D\u1E3E\u1E40\u1E42\u2C6E\u019C\u03FB",
13550           }, {
13551             base: 'N',
13552             chars: "\uA7A4\u0220\u24C3\uFF2E\u01F8\u0143\xD1\u1E44\u0147\u1E46\u0145\u1E4A\u1E48\u019D\uA790\u1D0E",
13553           }, {
13554             base: 'NJ',
13555             chars: "\u01CA",
13556           }, {
13557             base: 'Nj',
13558             chars: "\u01CB",
13559           }, {
13560             base: 'O',
13561             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",
13562           }, {
13563             base: 'OE',
13564             chars: "\u0152",
13565           }, {
13566             base: 'OI',
13567             chars: "\u01A2",
13568           }, {
13569             base: 'OO',
13570             chars: "\uA74E",
13571           }, {
13572             base: 'OU',
13573             chars: "\u0222",
13574           }, {
13575             base: 'P',
13576             chars: "\u24C5\uFF30\u1E54\u1E56\u01A4\u2C63\uA750\uA752\uA754",
13577           }, {
13578             base: 'Q',
13579             chars: "\u24C6\uFF31\uA756\uA758\u024A",
13580           }, {
13581             base: 'R',
13582             chars: "\u24C7\uFF32\u0154\u1E58\u0158\u0210\u0212\u1E5A\u1E5C\u0156\u1E5E\u024C\u2C64\uA75A\uA7A6\uA782",
13583           }, {
13584             base: 'S',
13585             chars: "\u24C8\uFF33\u1E9E\u015A\u1E64\u015C\u1E60\u0160\u1E66\u1E62\u1E68\u0218\u015E\u2C7E\uA7A8\uA784",
13586           }, {
13587             base: 'T',
13588             chars: "\u24C9\uFF34\u1E6A\u0164\u1E6C\u021A\u0162\u1E70\u1E6E\u0166\u01AC\u01AE\u023E\uA786",
13589           }, {
13590             base: 'Th',
13591             chars: "\u00DE",
13592           }, {
13593             base: 'TZ',
13594             chars: "\uA728",
13595           }, {
13596             base: 'U',
13597             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",
13598           }, {
13599             base: 'V',
13600             chars: "\u24CB\uFF36\u1E7C\u1E7E\u01B2\uA75E\u0245",
13601           }, {
13602             base: 'VY',
13603             chars: "\uA760",
13604           }, {
13605             base: 'W',
13606             chars: "\u24CC\uFF37\u1E80\u1E82\u0174\u1E86\u1E84\u1E88\u2C72",
13607           }, {
13608             base: 'X',
13609             chars: "\u24CD\uFF38\u1E8A\u1E8C",
13610           }, {
13611             base: 'Y',
13612             chars: "\u24CE\uFF39\u1EF2\xDD\u0176\u1EF8\u0232\u1E8E\u0178\u1EF6\u1EF4\u01B3\u024E\u1EFE",
13613           }, {
13614             base: 'Z',
13615             chars: "\u24CF\uFF3A\u0179\u1E90\u017B\u017D\u1E92\u1E94\u01B5\u0224\u2C7F\u2C6B\uA762",
13616           }, {
13617             base: 'a',
13618             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",
13619           }, {
13620             base: 'aa',
13621             chars: "\uA733",
13622           }, {
13623             base: 'ae',
13624             chars: "\u00E6\u01FD\u01E3",
13625           }, {
13626             base: 'ao',
13627             chars: "\uA735",
13628           }, {
13629             base: 'au',
13630             chars: "\uA737",
13631           }, {
13632             base: 'av',
13633             chars: "\uA739\uA73B",
13634           }, {
13635             base: 'ay',
13636             chars: "\uA73D",
13637           }, {
13638             base: 'b',
13639             chars: "\u24D1\uFF42\u1E03\u1E05\u1E07\u0180\u0183\u0253\u0182",
13640           }, {
13641             base: 'c',
13642             chars: "\uFF43\u24D2\u0107\u0109\u010B\u010D\u00E7\u1E09\u0188\u023C\uA73F\u2184",
13643           }, {
13644             base: 'd',
13645             chars: "\u24D3\uFF44\u1E0B\u010F\u1E0D\u1E11\u1E13\u1E0F\u0111\u018C\u0256\u0257\u018B\u13E7\u0501\uA7AA",
13646           }, {
13647             base: 'dh',
13648             chars: "\u00F0",
13649           }, {
13650             base: 'dz',
13651             chars: "\u01F3\u01C6",
13652           }, {
13653             base: 'e',
13654             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",
13655           }, {
13656             base: 'f',
13657             chars: "\u24D5\uFF46\u1E1F\u0192",
13658           }, {
13659             base: 'ff',
13660             chars: "\uFB00",
13661           }, {
13662             base: 'fi',
13663             chars: "\uFB01",
13664           }, {
13665             base: 'fl',
13666             chars: "\uFB02",
13667           }, {
13668             base: 'ffi',
13669             chars: "\uFB03",
13670           }, {
13671             base: 'ffl',
13672             chars: "\uFB04",
13673           }, {
13674             base: 'g',
13675             chars: "\u24D6\uFF47\u01F5\u011D\u1E21\u011F\u0121\u01E7\u0123\u01E5\u0260\uA7A1\uA77F\u1D79",
13676           }, {
13677             base: 'h',
13678             chars: "\u24D7\uFF48\u0125\u1E23\u1E27\u021F\u1E25\u1E29\u1E2B\u1E96\u0127\u2C68\u2C76\u0265",
13679           }, {
13680             base: 'hv',
13681             chars: "\u0195",
13682           }, {
13683             base: 'i',
13684             chars: "\u24D8\uFF49\xEC\xED\xEE\u0129\u012B\u012D\xEF\u1E2F\u1EC9\u01D0\u0209\u020B\u1ECB\u012F\u1E2D\u0268\u0131",
13685           }, {
13686             base: 'j',
13687             chars: "\u24D9\uFF4A\u0135\u01F0\u0249",
13688           }, {
13689             base: 'k',
13690             chars: "\u24DA\uFF4B\u1E31\u01E9\u1E33\u0137\u1E35\u0199\u2C6A\uA741\uA743\uA745\uA7A3",
13691           }, {
13692             base: 'l',
13693             chars: "\u24DB\uFF4C\u0140\u013A\u013E\u1E37\u1E39\u013C\u1E3D\u1E3B\u017F\u0142\u019A\u026B\u2C61\uA749\uA781\uA747\u026D",
13694           }, {
13695             base: 'lj',
13696             chars: "\u01C9",
13697           }, {
13698             base: 'm',
13699             chars: "\u24DC\uFF4D\u1E3F\u1E41\u1E43\u0271\u026F",
13700           }, {
13701             base: 'n',
13702             chars: "\u24DD\uFF4E\u01F9\u0144\xF1\u1E45\u0148\u1E47\u0146\u1E4B\u1E49\u019E\u0272\u0149\uA791\uA7A5\u043B\u0509",
13703           }, {
13704             base: 'nj',
13705             chars: "\u01CC",
13706           }, {
13707             base: 'o',
13708             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",
13709           }, {
13710             base: 'oe',
13711             chars: "\u0153",
13712           }, {
13713             base: 'oi',
13714             chars: "\u01A3",
13715           }, {
13716             base: 'oo',
13717             chars: "\uA74F",
13718           }, {
13719             base: 'ou',
13720             chars: "\u0223",
13721           }, {
13722             base: 'p',
13723             chars: "\u24DF\uFF50\u1E55\u1E57\u01A5\u1D7D\uA751\uA753\uA755\u03C1",
13724           }, {
13725             base: 'q',
13726             chars: "\u24E0\uFF51\u024B\uA757\uA759",
13727           }, {
13728             base: 'r',
13729             chars: "\u24E1\uFF52\u0155\u1E59\u0159\u0211\u0213\u1E5B\u1E5D\u0157\u1E5F\u024D\u027D\uA75B\uA7A7\uA783",
13730           }, {
13731             base: 's',
13732             chars: "\u24E2\uFF53\u015B\u1E65\u015D\u1E61\u0161\u1E67\u1E63\u1E69\u0219\u015F\u023F\uA7A9\uA785\u1E9B\u0282",
13733           }, {
13734             base: 'ss',
13735             chars: "\xDF",
13736           }, {
13737             base: 't',
13738             chars: "\u24E3\uFF54\u1E6B\u1E97\u0165\u1E6D\u021B\u0163\u1E71\u1E6F\u0167\u01AD\u0288\u2C66\uA787",
13739           }, {
13740             base: 'th',
13741             chars: "\u00FE",
13742           }, {
13743             base: 'tz',
13744             chars: "\uA729",
13745           }, {
13746             base: 'u',
13747             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",
13748           }, {
13749             base: 'v',
13750             chars: "\u24E5\uFF56\u1E7D\u1E7F\u028B\uA75F\u028C",
13751           }, {
13752             base: 'vy',
13753             chars: "\uA761",
13754           }, {
13755             base: 'w',
13756             chars: "\u24E6\uFF57\u1E81\u1E83\u0175\u1E87\u1E85\u1E98\u1E89\u2C73",
13757           }, {
13758             base: 'x',
13759             chars: "\u24E7\uFF58\u1E8B\u1E8D",
13760           }, {
13761             base: 'y',
13762             chars: "\u24E8\uFF59\u1EF3\xFD\u0177\u1EF9\u0233\u1E8F\xFF\u1EF7\u1E99\u1EF5\u01B4\u024F\u1EFF",
13763           }, {
13764             base: 'z',
13765             chars: "\u24E9\uFF5A\u017A\u1E91\u017C\u017E\u1E93\u1E95\u01B6\u0225\u0240\u2C6C\uA763",
13766           }
13767         ];
13768
13769         var diacriticsMap = {};
13770         for (var i$1 = 0; i$1 < replacementList.length; i$1 += 1) {
13771           var chars = replacementList[i$1].chars;
13772           for (var j = 0; j < chars.length; j += 1) {
13773             diacriticsMap[chars[j]] = replacementList[i$1].base;
13774           }
13775         }
13776
13777         function removeDiacritics(str) {
13778           return str.replace(/[^\u0000-\u007e]/g, function(c) {
13779             return diacriticsMap[c] || c;
13780           });
13781         }
13782
13783         var replacementList_1 = replacementList;
13784         var diacriticsMap_1 = diacriticsMap;
13785
13786         var diacritics = {
13787                 remove: remove$1,
13788                 replacementList: replacementList_1,
13789                 diacriticsMap: diacriticsMap_1
13790         };
13791
13792         var isArabic_1 = createCommonjsModule(function (module, exports) {
13793         Object.defineProperty(exports, "__esModule", { value: true });
13794         var arabicBlocks = [
13795             [0x0600, 0x06FF],
13796             [0x0750, 0x077F],
13797             [0x08A0, 0x08FF],
13798             [0xFB50, 0xFDFF],
13799             [0xFE70, 0xFEFF],
13800             [0x10E60, 0x10E7F],
13801             [0x1EC70, 0x1ECBF],
13802             [0x1EE00, 0x1EEFF] // Mathematical Alphabetic symbols https://www.unicode.org/charts/PDF/U1EE00.pdf
13803         ];
13804         function isArabic(char) {
13805             if (char.length > 1) {
13806                 // allow the newer chars?
13807                 throw new Error('isArabic works on only one-character strings');
13808             }
13809             var code = char.charCodeAt(0);
13810             for (var i = 0; i < arabicBlocks.length; i++) {
13811                 var block = arabicBlocks[i];
13812                 if (code >= block[0] && code <= block[1]) {
13813                     return true;
13814                 }
13815             }
13816             return false;
13817         }
13818         exports.isArabic = isArabic;
13819         function isMath(char) {
13820             if (char.length > 2) {
13821                 // allow the newer chars?
13822                 throw new Error('isMath works on only one-character strings');
13823             }
13824             var code = char.charCodeAt(0);
13825             return ((code >= 0x660 && code <= 0x66C) || (code >= 0x6F0 && code <= 0x6F9));
13826         }
13827         exports.isMath = isMath;
13828         });
13829
13830         var unicodeArabic = createCommonjsModule(function (module, exports) {
13831         Object.defineProperty(exports, "__esModule", { value: true });
13832         var arabicReference = {
13833             "alef": {
13834                 "normal": [
13835                     "\u0627"
13836                 ],
13837                 "madda_above": {
13838                     "normal": [
13839                         "\u0627\u0653",
13840                         "\u0622"
13841                     ],
13842                     "isolated": "\uFE81",
13843                     "final": "\uFE82"
13844                 },
13845                 "hamza_above": {
13846                     "normal": [
13847                         "\u0627\u0654",
13848                         "\u0623"
13849                     ],
13850                     "isolated": "\uFE83",
13851                     "final": "\uFE84"
13852                 },
13853                 "hamza_below": {
13854                     "normal": [
13855                         "\u0627\u0655",
13856                         "\u0625"
13857                     ],
13858                     "isolated": "\uFE87",
13859                     "final": "\uFE88"
13860                 },
13861                 "wasla": {
13862                     "normal": "\u0671",
13863                     "isolated": "\uFB50",
13864                     "final": "\uFB51"
13865                 },
13866                 "wavy_hamza_above": [
13867                     "\u0672"
13868                 ],
13869                 "wavy_hamza_below": [
13870                     "\u0627\u065F",
13871                     "\u0673"
13872                 ],
13873                 "high_hamza": [
13874                     "\u0675",
13875                     "\u0627\u0674"
13876                 ],
13877                 "indic_two_above": [
13878                     "\u0773"
13879                 ],
13880                 "indic_three_above": [
13881                     "\u0774"
13882                 ],
13883                 "fathatan": {
13884                     "normal": [
13885                         "\u0627\u064B"
13886                     ],
13887                     "final": "\uFD3C",
13888                     "isolated": "\uFD3D"
13889                 },
13890                 "isolated": "\uFE8D",
13891                 "final": "\uFE8E"
13892             },
13893             "beh": {
13894                 "normal": [
13895                     "\u0628"
13896                 ],
13897                 "dotless": [
13898                     "\u066E"
13899                 ],
13900                 "three_dots_horizontally_below": [
13901                     "\u0750"
13902                 ],
13903                 "dot_below_three_dots_above": [
13904                     "\u0751"
13905                 ],
13906                 "three_dots_pointing_upwards_below": [
13907                     "\u0752"
13908                 ],
13909                 "three_dots_pointing_upwards_below_two_dots_above": [
13910                     "\u0753"
13911                 ],
13912                 "two_dots_below_dot_above": [
13913                     "\u0754"
13914                 ],
13915                 "inverted_small_v_below": [
13916                     "\u0755"
13917                 ],
13918                 "small_v": [
13919                     "\u0756"
13920                 ],
13921                 "small_v_below": [
13922                     "\u08A0"
13923                 ],
13924                 "hamza_above": [
13925                     "\u08A1"
13926                 ],
13927                 "small_meem_above": [
13928                     "\u08B6"
13929                 ],
13930                 "isolated": "\uFE8F",
13931                 "final": "\uFE90",
13932                 "initial": "\uFE91",
13933                 "medial": "\uFE92"
13934             },
13935             "teh marbuta": {
13936                 "normal": [
13937                     "\u0629"
13938                 ],
13939                 "isolated": "\uFE93",
13940                 "final": "\uFE94"
13941             },
13942             "teh": {
13943                 "normal": [
13944                     "\u062A"
13945                 ],
13946                 "ring": [
13947                     "\u067C"
13948                 ],
13949                 "three_dots_above_downwards": [
13950                     "\u067D"
13951                 ],
13952                 "small_teh_above": [
13953                     "\u08B8"
13954                 ],
13955                 "isolated": "\uFE95",
13956                 "final": "\uFE96",
13957                 "initial": "\uFE97",
13958                 "medial": "\uFE98"
13959             },
13960             "theh": {
13961                 "normal": [
13962                     "\u062B"
13963                 ],
13964                 "isolated": "\uFE99",
13965                 "final": "\uFE9A",
13966                 "initial": "\uFE9B",
13967                 "medial": "\uFE9C"
13968             },
13969             "jeem": {
13970                 "normal": [
13971                     "\u062C"
13972                 ],
13973                 "two_dots_above": [
13974                     "\u08A2"
13975                 ],
13976                 "isolated": "\uFE9D",
13977                 "final": "\uFE9E",
13978                 "initial": "\uFE9F",
13979                 "medial": "\uFEA0"
13980             },
13981             "hah": {
13982                 "normal": [
13983                     "\u062D"
13984                 ],
13985                 "hamza_above": [
13986                     "\u0681"
13987                 ],
13988                 "two_dots_vertical_above": [
13989                     "\u0682"
13990                 ],
13991                 "three_dots_above": [
13992                     "\u0685"
13993                 ],
13994                 "two_dots_above": [
13995                     "\u0757"
13996                 ],
13997                 "three_dots_pointing_upwards_below": [
13998                     "\u0758"
13999                 ],
14000                 "small_tah_below": [
14001                     "\u076E"
14002                 ],
14003                 "small_tah_two_dots": [
14004                     "\u076F"
14005                 ],
14006                 "small_tah_above": [
14007                     "\u0772"
14008                 ],
14009                 "indic_four_below": [
14010                     "\u077C"
14011                 ],
14012                 "isolated": "\uFEA1",
14013                 "final": "\uFEA2",
14014                 "initial": "\uFEA3",
14015                 "medial": "\uFEA4"
14016             },
14017             "khah": {
14018                 "normal": [
14019                     "\u062E"
14020                 ],
14021                 "isolated": "\uFEA5",
14022                 "final": "\uFEA6",
14023                 "initial": "\uFEA7",
14024                 "medial": "\uFEA8"
14025             },
14026             "dal": {
14027                 "normal": [
14028                     "\u062F"
14029                 ],
14030                 "ring": [
14031                     "\u0689"
14032                 ],
14033                 "dot_below": [
14034                     "\u068A"
14035                 ],
14036                 "dot_below_small_tah": [
14037                     "\u068B"
14038                 ],
14039                 "three_dots_above_downwards": [
14040                     "\u068F"
14041                 ],
14042                 "four_dots_above": [
14043                     "\u0690"
14044                 ],
14045                 "inverted_v": [
14046                     "\u06EE"
14047                 ],
14048                 "two_dots_vertically_below_small_tah": [
14049                     "\u0759"
14050                 ],
14051                 "inverted_small_v_below": [
14052                     "\u075A"
14053                 ],
14054                 "three_dots_below": [
14055                     "\u08AE"
14056                 ],
14057                 "isolated": "\uFEA9",
14058                 "final": "\uFEAA"
14059             },
14060             "thal": {
14061                 "normal": [
14062                     "\u0630"
14063                 ],
14064                 "isolated": "\uFEAB",
14065                 "final": "\uFEAC"
14066             },
14067             "reh": {
14068                 "normal": [
14069                     "\u0631"
14070                 ],
14071                 "small_v": [
14072                     "\u0692"
14073                 ],
14074                 "ring": [
14075                     "\u0693"
14076                 ],
14077                 "dot_below": [
14078                     "\u0694"
14079                 ],
14080                 "small_v_below": [
14081                     "\u0695"
14082                 ],
14083                 "dot_below_dot_above": [
14084                     "\u0696"
14085                 ],
14086                 "two_dots_above": [
14087                     "\u0697"
14088                 ],
14089                 "four_dots_above": [
14090                     "\u0699"
14091                 ],
14092                 "inverted_v": [
14093                     "\u06EF"
14094                 ],
14095                 "stroke": [
14096                     "\u075B"
14097                 ],
14098                 "two_dots_vertically_above": [
14099                     "\u076B"
14100                 ],
14101                 "hamza_above": [
14102                     "\u076C"
14103                 ],
14104                 "small_tah_two_dots": [
14105                     "\u0771"
14106                 ],
14107                 "loop": [
14108                     "\u08AA"
14109                 ],
14110                 "small_noon_above": [
14111                     "\u08B9"
14112                 ],
14113                 "isolated": "\uFEAD",
14114                 "final": "\uFEAE"
14115             },
14116             "zain": {
14117                 "normal": [
14118                     "\u0632"
14119                 ],
14120                 "inverted_v_above": [
14121                     "\u08B2"
14122                 ],
14123                 "isolated": "\uFEAF",
14124                 "final": "\uFEB0"
14125             },
14126             "seen": {
14127                 "normal": [
14128                     "\u0633"
14129                 ],
14130                 "dot_below_dot_above": [
14131                     "\u069A"
14132                 ],
14133                 "three_dots_below": [
14134                     "\u069B"
14135                 ],
14136                 "three_dots_below_three_dots_above": [
14137                     "\u069C"
14138                 ],
14139                 "four_dots_above": [
14140                     "\u075C"
14141                 ],
14142                 "two_dots_vertically_above": [
14143                     "\u076D"
14144                 ],
14145                 "small_tah_two_dots": [
14146                     "\u0770"
14147                 ],
14148                 "indic_four_above": [
14149                     "\u077D"
14150                 ],
14151                 "inverted_v": [
14152                     "\u077E"
14153                 ],
14154                 "isolated": "\uFEB1",
14155                 "final": "\uFEB2",
14156                 "initial": "\uFEB3",
14157                 "medial": "\uFEB4"
14158             },
14159             "sheen": {
14160                 "normal": [
14161                     "\u0634"
14162                 ],
14163                 "dot_below": [
14164                     "\u06FA"
14165                 ],
14166                 "isolated": "\uFEB5",
14167                 "final": "\uFEB6",
14168                 "initial": "\uFEB7",
14169                 "medial": "\uFEB8"
14170             },
14171             "sad": {
14172                 "normal": [
14173                     "\u0635"
14174                 ],
14175                 "two_dots_below": [
14176                     "\u069D"
14177                 ],
14178                 "three_dots_above": [
14179                     "\u069E"
14180                 ],
14181                 "three_dots_below": [
14182                     "\u08AF"
14183                 ],
14184                 "isolated": "\uFEB9",
14185                 "final": "\uFEBA",
14186                 "initial": "\uFEBB",
14187                 "medial": "\uFEBC"
14188             },
14189             "dad": {
14190                 "normal": [
14191                     "\u0636"
14192                 ],
14193                 "dot_below": [
14194                     "\u06FB"
14195                 ],
14196                 "isolated": "\uFEBD",
14197                 "final": "\uFEBE",
14198                 "initial": "\uFEBF",
14199                 "medial": "\uFEC0"
14200             },
14201             "tah": {
14202                 "normal": [
14203                     "\u0637"
14204                 ],
14205                 "three_dots_above": [
14206                     "\u069F"
14207                 ],
14208                 "two_dots_above": [
14209                     "\u08A3"
14210                 ],
14211                 "isolated": "\uFEC1",
14212                 "final": "\uFEC2",
14213                 "initial": "\uFEC3",
14214                 "medial": "\uFEC4"
14215             },
14216             "zah": {
14217                 "normal": [
14218                     "\u0638"
14219                 ],
14220                 "isolated": "\uFEC5",
14221                 "final": "\uFEC6",
14222                 "initial": "\uFEC7",
14223                 "medial": "\uFEC8"
14224             },
14225             "ain": {
14226                 "normal": [
14227                     "\u0639"
14228                 ],
14229                 "three_dots_above": [
14230                     "\u06A0"
14231                 ],
14232                 "two_dots_above": [
14233                     "\u075D"
14234                 ],
14235                 "three_dots_pointing_downwards_above": [
14236                     "\u075E"
14237                 ],
14238                 "two_dots_vertically_above": [
14239                     "\u075F"
14240                 ],
14241                 "three_dots_below": [
14242                     "\u08B3"
14243                 ],
14244                 "isolated": "\uFEC9",
14245                 "final": "\uFECA",
14246                 "initial": "\uFECB",
14247                 "medial": "\uFECC"
14248             },
14249             "ghain": {
14250                 "normal": [
14251                     "\u063A"
14252                 ],
14253                 "dot_below": [
14254                     "\u06FC"
14255                 ],
14256                 "isolated": "\uFECD",
14257                 "final": "\uFECE",
14258                 "initial": "\uFECF",
14259                 "medial": "\uFED0"
14260             },
14261             "feh": {
14262                 "normal": [
14263                     "\u0641"
14264                 ],
14265                 "dotless": [
14266                     "\u06A1"
14267                 ],
14268                 "dot_moved_below": [
14269                     "\u06A2"
14270                 ],
14271                 "dot_below": [
14272                     "\u06A3"
14273                 ],
14274                 "three_dots_below": [
14275                     "\u06A5"
14276                 ],
14277                 "two_dots_below": [
14278                     "\u0760"
14279                 ],
14280                 "three_dots_pointing_upwards_below": [
14281                     "\u0761"
14282                 ],
14283                 "dot_below_three_dots_above": [
14284                     "\u08A4"
14285                 ],
14286                 "isolated": "\uFED1",
14287                 "final": "\uFED2",
14288                 "initial": "\uFED3",
14289                 "medial": "\uFED4"
14290             },
14291             "qaf": {
14292                 "normal": [
14293                     "\u0642"
14294                 ],
14295                 "dotless": [
14296                     "\u066F"
14297                 ],
14298                 "dot_above": [
14299                     "\u06A7"
14300                 ],
14301                 "three_dots_above": [
14302                     "\u06A8"
14303                 ],
14304                 "dot_below": [
14305                     "\u08A5"
14306                 ],
14307                 "isolated": "\uFED5",
14308                 "final": "\uFED6",
14309                 "initial": "\uFED7",
14310                 "medial": "\uFED8"
14311             },
14312             "kaf": {
14313                 "normal": [
14314                     "\u0643"
14315                 ],
14316                 "swash": [
14317                     "\u06AA"
14318                 ],
14319                 "ring": [
14320                     "\u06AB"
14321                 ],
14322                 "dot_above": [
14323                     "\u06AC"
14324                 ],
14325                 "three_dots_below": [
14326                     "\u06AE"
14327                 ],
14328                 "two_dots_above": [
14329                     "\u077F"
14330                 ],
14331                 "dot_below": [
14332                     "\u08B4"
14333                 ],
14334                 "isolated": "\uFED9",
14335                 "final": "\uFEDA",
14336                 "initial": "\uFEDB",
14337                 "medial": "\uFEDC"
14338             },
14339             "lam": {
14340                 "normal": [
14341                     "\u0644"
14342                 ],
14343                 "small_v": [
14344                     "\u06B5"
14345                 ],
14346                 "dot_above": [
14347                     "\u06B6"
14348                 ],
14349                 "three_dots_above": [
14350                     "\u06B7"
14351                 ],
14352                 "three_dots_below": [
14353                     "\u06B8"
14354                 ],
14355                 "bar": [
14356                     "\u076A"
14357                 ],
14358                 "double_bar": [
14359                     "\u08A6"
14360                 ],
14361                 "isolated": "\uFEDD",
14362                 "final": "\uFEDE",
14363                 "initial": "\uFEDF",
14364                 "medial": "\uFEE0"
14365             },
14366             "meem": {
14367                 "normal": [
14368                     "\u0645"
14369                 ],
14370                 "dot_above": [
14371                     "\u0765"
14372                 ],
14373                 "dot_below": [
14374                     "\u0766"
14375                 ],
14376                 "three_dots_above": [
14377                     "\u08A7"
14378                 ],
14379                 "isolated": "\uFEE1",
14380                 "final": "\uFEE2",
14381                 "initial": "\uFEE3",
14382                 "medial": "\uFEE4"
14383             },
14384             "noon": {
14385                 "normal": [
14386                     "\u0646"
14387                 ],
14388                 "dot_below": [
14389                     "\u06B9"
14390                 ],
14391                 "ring": [
14392                     "\u06BC"
14393                 ],
14394                 "three_dots_above": [
14395                     "\u06BD"
14396                 ],
14397                 "two_dots_below": [
14398                     "\u0767"
14399                 ],
14400                 "small_tah": [
14401                     "\u0768"
14402                 ],
14403                 "small_v": [
14404                     "\u0769"
14405                 ],
14406                 "isolated": "\uFEE5",
14407                 "final": "\uFEE6",
14408                 "initial": "\uFEE7",
14409                 "medial": "\uFEE8"
14410             },
14411             "heh": {
14412                 "normal": [
14413                     "\u0647"
14414                 ],
14415                 "isolated": "\uFEE9",
14416                 "final": "\uFEEA",
14417                 "initial": "\uFEEB",
14418                 "medial": "\uFEEC"
14419             },
14420             "waw": {
14421                 "normal": [
14422                     "\u0648"
14423                 ],
14424                 "hamza_above": {
14425                     "normal": [
14426                         "\u0624",
14427                         "\u0648\u0654"
14428                     ],
14429                     "isolated": "\uFE85",
14430                     "final": "\uFE86"
14431                 },
14432                 "high_hamza": [
14433                     "\u0676",
14434                     "\u0648\u0674"
14435                 ],
14436                 "ring": [
14437                     "\u06C4"
14438                 ],
14439                 "two_dots_above": [
14440                     "\u06CA"
14441                 ],
14442                 "dot_above": [
14443                     "\u06CF"
14444                 ],
14445                 "indic_two_above": [
14446                     "\u0778"
14447                 ],
14448                 "indic_three_above": [
14449                     "\u0779"
14450                 ],
14451                 "dot_within": [
14452                     "\u08AB"
14453                 ],
14454                 "isolated": "\uFEED",
14455                 "final": "\uFEEE"
14456             },
14457             "alef_maksura": {
14458                 "normal": [
14459                     "\u0649"
14460                 ],
14461                 "hamza_above": [
14462                     "\u0626",
14463                     "\u064A\u0654"
14464                 ],
14465                 "initial": "\uFBE8",
14466                 "medial": "\uFBE9",
14467                 "isolated": "\uFEEF",
14468                 "final": "\uFEF0"
14469             },
14470             "yeh": {
14471                 "normal": [
14472                     "\u064A"
14473                 ],
14474                 "hamza_above": {
14475                     "normal": [
14476                         "\u0626",
14477                         "\u0649\u0654"
14478                     ],
14479                     "isolated": "\uFE89",
14480                     "final": "\uFE8A",
14481                     "initial": "\uFE8B",
14482                     "medial": "\uFE8C"
14483                 },
14484                 "two_dots_below_hamza_above": [
14485                     "\u08A8"
14486                 ],
14487                 "high_hamza": [
14488                     "\u0678",
14489                     "\u064A\u0674"
14490                 ],
14491                 "tail": [
14492                     "\u06CD"
14493                 ],
14494                 "small_v": [
14495                     "\u06CE"
14496                 ],
14497                 "three_dots_below": [
14498                     "\u06D1"
14499                 ],
14500                 "two_dots_below_dot_above": [
14501                     "\u08A9"
14502                 ],
14503                 "two_dots_below_small_noon_above": [
14504                     "\u08BA"
14505                 ],
14506                 "isolated": "\uFEF1",
14507                 "final": "\uFEF2",
14508                 "initial": "\uFEF3",
14509                 "medial": "\uFEF4"
14510             },
14511             "tteh": {
14512                 "normal": [
14513                     "\u0679"
14514                 ],
14515                 "isolated": "\uFB66",
14516                 "final": "\uFB67",
14517                 "initial": "\uFB68",
14518                 "medial": "\uFB69"
14519             },
14520             "tteheh": {
14521                 "normal": [
14522                     "\u067A"
14523                 ],
14524                 "isolated": "\uFB5E",
14525                 "final": "\uFB5F",
14526                 "initial": "\uFB60",
14527                 "medial": "\uFB61"
14528             },
14529             "beeh": {
14530                 "normal": [
14531                     "\u067B"
14532                 ],
14533                 "isolated": "\uFB52",
14534                 "final": "\uFB53",
14535                 "initial": "\uFB54",
14536                 "medial": "\uFB55"
14537             },
14538             "peh": {
14539                 "normal": [
14540                     "\u067E"
14541                 ],
14542                 "small_meem_above": [
14543                     "\u08B7"
14544                 ],
14545                 "isolated": "\uFB56",
14546                 "final": "\uFB57",
14547                 "initial": "\uFB58",
14548                 "medial": "\uFB59"
14549             },
14550             "teheh": {
14551                 "normal": [
14552                     "\u067F"
14553                 ],
14554                 "isolated": "\uFB62",
14555                 "final": "\uFB63",
14556                 "initial": "\uFB64",
14557                 "medial": "\uFB65"
14558             },
14559             "beheh": {
14560                 "normal": [
14561                     "\u0680"
14562                 ],
14563                 "isolated": "\uFB5A",
14564                 "final": "\uFB5B",
14565                 "initial": "\uFB5C",
14566                 "medial": "\uFB5D"
14567             },
14568             "nyeh": {
14569                 "normal": [
14570                     "\u0683"
14571                 ],
14572                 "isolated": "\uFB76",
14573                 "final": "\uFB77",
14574                 "initial": "\uFB78",
14575                 "medial": "\uFB79"
14576             },
14577             "dyeh": {
14578                 "normal": [
14579                     "\u0684"
14580                 ],
14581                 "isolated": "\uFB72",
14582                 "final": "\uFB73",
14583                 "initial": "\uFB74",
14584                 "medial": "\uFB75"
14585             },
14586             "tcheh": {
14587                 "normal": [
14588                     "\u0686"
14589                 ],
14590                 "dot_above": [
14591                     "\u06BF"
14592                 ],
14593                 "isolated": "\uFB7A",
14594                 "final": "\uFB7B",
14595                 "initial": "\uFB7C",
14596                 "medial": "\uFB7D"
14597             },
14598             "tcheheh": {
14599                 "normal": [
14600                     "\u0687"
14601                 ],
14602                 "isolated": "\uFB7E",
14603                 "final": "\uFB7F",
14604                 "initial": "\uFB80",
14605                 "medial": "\uFB81"
14606             },
14607             "ddal": {
14608                 "normal": [
14609                     "\u0688"
14610                 ],
14611                 "isolated": "\uFB88",
14612                 "final": "\uFB89"
14613             },
14614             "dahal": {
14615                 "normal": [
14616                     "\u068C"
14617                 ],
14618                 "isolated": "\uFB84",
14619                 "final": "\uFB85"
14620             },
14621             "ddahal": {
14622                 "normal": [
14623                     "\u068D"
14624                 ],
14625                 "isolated": "\uFB82",
14626                 "final": "\uFB83"
14627             },
14628             "dul": {
14629                 "normal": [
14630                     "\u068F",
14631                     "\u068E"
14632                 ],
14633                 "isolated": "\uFB86",
14634                 "final": "\uFB87"
14635             },
14636             "rreh": {
14637                 "normal": [
14638                     "\u0691"
14639                 ],
14640                 "isolated": "\uFB8C",
14641                 "final": "\uFB8D"
14642             },
14643             "jeh": {
14644                 "normal": [
14645                     "\u0698"
14646                 ],
14647                 "isolated": "\uFB8A",
14648                 "final": "\uFB8B"
14649             },
14650             "veh": {
14651                 "normal": [
14652                     "\u06A4"
14653                 ],
14654                 "isolated": "\uFB6A",
14655                 "final": "\uFB6B",
14656                 "initial": "\uFB6C",
14657                 "medial": "\uFB6D"
14658             },
14659             "peheh": {
14660                 "normal": [
14661                     "\u06A6"
14662                 ],
14663                 "isolated": "\uFB6E",
14664                 "final": "\uFB6F",
14665                 "initial": "\uFB70",
14666                 "medial": "\uFB71"
14667             },
14668             "keheh": {
14669                 "normal": [
14670                     "\u06A9"
14671                 ],
14672                 "dot_above": [
14673                     "\u0762"
14674                 ],
14675                 "three_dots_above": [
14676                     "\u0763"
14677                 ],
14678                 "three_dots_pointing_upwards_below": [
14679                     "\u0764"
14680                 ],
14681                 "isolated": "\uFB8E",
14682                 "final": "\uFB8F",
14683                 "initial": "\uFB90",
14684                 "medial": "\uFB91"
14685             },
14686             "ng": {
14687                 "normal": [
14688                     "\u06AD"
14689                 ],
14690                 "isolated": "\uFBD3",
14691                 "final": "\uFBD4",
14692                 "initial": "\uFBD5",
14693                 "medial": "\uFBD6"
14694             },
14695             "gaf": {
14696                 "normal": [
14697                     "\u06AF"
14698                 ],
14699                 "ring": [
14700                     "\u06B0"
14701                 ],
14702                 "two_dots_below": [
14703                     "\u06B2"
14704                 ],
14705                 "three_dots_above": [
14706                     "\u06B4"
14707                 ],
14708                 "inverted_stroke": [
14709                     "\u08B0"
14710                 ],
14711                 "isolated": "\uFB92",
14712                 "final": "\uFB93",
14713                 "initial": "\uFB94",
14714                 "medial": "\uFB95"
14715             },
14716             "ngoeh": {
14717                 "normal": [
14718                     "\u06B1"
14719                 ],
14720                 "isolated": "\uFB9A",
14721                 "final": "\uFB9B",
14722                 "initial": "\uFB9C",
14723                 "medial": "\uFB9D"
14724             },
14725             "gueh": {
14726                 "normal": [
14727                     "\u06B3"
14728                 ],
14729                 "isolated": "\uFB96",
14730                 "final": "\uFB97",
14731                 "initial": "\uFB98",
14732                 "medial": "\uFB99"
14733             },
14734             "noon ghunna": {
14735                 "normal": [
14736                     "\u06BA"
14737                 ],
14738                 "isolated": "\uFB9E",
14739                 "final": "\uFB9F"
14740             },
14741             "rnoon": {
14742                 "normal": [
14743                     "\u06BB"
14744                 ],
14745                 "isolated": "\uFBA0",
14746                 "final": "\uFBA1",
14747                 "initial": "\uFBA2",
14748                 "medial": "\uFBA3"
14749             },
14750             "heh doachashmee": {
14751                 "normal": [
14752                     "\u06BE"
14753                 ],
14754                 "isolated": "\uFBAA",
14755                 "final": "\uFBAB",
14756                 "initial": "\uFBAC",
14757                 "medial": "\uFBAD"
14758             },
14759             "heh goal": {
14760                 "normal": [
14761                     "\u06C1"
14762                 ],
14763                 "hamza_above": [
14764                     "\u06C1\u0654",
14765                     "\u06C2"
14766                 ],
14767                 "isolated": "\uFBA6",
14768                 "final": "\uFBA7",
14769                 "initial": "\uFBA8",
14770                 "medial": "\uFBA9"
14771             },
14772             "teh marbuta goal": {
14773                 "normal": [
14774                     "\u06C3"
14775                 ]
14776             },
14777             "kirghiz oe": {
14778                 "normal": [
14779                     "\u06C5"
14780                 ],
14781                 "isolated": "\uFBE0",
14782                 "final": "\uFBE1"
14783             },
14784             "oe": {
14785                 "normal": [
14786                     "\u06C6"
14787                 ],
14788                 "isolated": "\uFBD9",
14789                 "final": "\uFBDA"
14790             },
14791             "u": {
14792                 "normal": [
14793                     "\u06C7"
14794                 ],
14795                 "hamza_above": {
14796                     "normal": [
14797                         "\u0677",
14798                         "\u06C7\u0674"
14799                     ],
14800                     "isolated": "\uFBDD"
14801                 },
14802                 "isolated": "\uFBD7",
14803                 "final": "\uFBD8"
14804             },
14805             "yu": {
14806                 "normal": [
14807                     "\u06C8"
14808                 ],
14809                 "isolated": "\uFBDB",
14810                 "final": "\uFBDC"
14811             },
14812             "kirghiz yu": {
14813                 "normal": [
14814                     "\u06C9"
14815                 ],
14816                 "isolated": "\uFBE2",
14817                 "final": "\uFBE3"
14818             },
14819             "ve": {
14820                 "normal": [
14821                     "\u06CB"
14822                 ],
14823                 "isolated": "\uFBDE",
14824                 "final": "\uFBDF"
14825             },
14826             "farsi yeh": {
14827                 "normal": [
14828                     "\u06CC"
14829                 ],
14830                 "indic_two_above": [
14831                     "\u0775"
14832                 ],
14833                 "indic_three_above": [
14834                     "\u0776"
14835                 ],
14836                 "indic_four_above": [
14837                     "\u0777"
14838                 ],
14839                 "isolated": "\uFBFC",
14840                 "final": "\uFBFD",
14841                 "initial": "\uFBFE",
14842                 "medial": "\uFBFF"
14843             },
14844             "e": {
14845                 "normal": [
14846                     "\u06D0"
14847                 ],
14848                 "isolated": "\uFBE4",
14849                 "final": "\uFBE5",
14850                 "initial": "\uFBE6",
14851                 "medial": "\uFBE7"
14852             },
14853             "yeh barree": {
14854                 "normal": [
14855                     "\u06D2"
14856                 ],
14857                 "hamza_above": {
14858                     "normal": [
14859                         "\u06D2\u0654",
14860                         "\u06D3"
14861                     ],
14862                     "isolated": "\uFBB0",
14863                     "final": "\uFBB1"
14864                 },
14865                 "indic_two_above": [
14866                     "\u077A"
14867                 ],
14868                 "indic_three_above": [
14869                     "\u077B"
14870                 ],
14871                 "isolated": "\uFBAE",
14872                 "final": "\uFBAF"
14873             },
14874             "ae": {
14875                 "normal": [
14876                     "\u06D5"
14877                 ],
14878                 "isolated": "\u06D5",
14879                 "final": "\uFEEA",
14880                 "yeh_above": {
14881                     "normal": [
14882                         "\u06C0",
14883                         "\u06D5\u0654"
14884                     ],
14885                     "isolated": "\uFBA4",
14886                     "final": "\uFBA5"
14887                 }
14888             },
14889             "rohingya yeh": {
14890                 "normal": [
14891                     "\u08AC"
14892                 ]
14893             },
14894             "low alef": {
14895                 "normal": [
14896                     "\u08AD"
14897                 ]
14898             },
14899             "straight waw": {
14900                 "normal": [
14901                     "\u08B1"
14902                 ]
14903             },
14904             "african feh": {
14905                 "normal": [
14906                     "\u08BB"
14907                 ]
14908             },
14909             "african qaf": {
14910                 "normal": [
14911                     "\u08BC"
14912                 ]
14913             },
14914             "african noon": {
14915                 "normal": [
14916                     "\u08BD"
14917                 ]
14918             }
14919         };
14920         exports.default = arabicReference;
14921         });
14922
14923         var unicodeLigatures = createCommonjsModule(function (module, exports) {
14924         Object.defineProperty(exports, "__esModule", { value: true });
14925         var ligatureReference = {
14926             "\u0626\u0627": {
14927                 "isolated": "\uFBEA",
14928                 "final": "\uFBEB"
14929             },
14930             "\u0626\u06D5": {
14931                 "isolated": "\uFBEC",
14932                 "final": "\uFBED"
14933             },
14934             "\u0626\u0648": {
14935                 "isolated": "\uFBEE",
14936                 "final": "\uFBEF"
14937             },
14938             "\u0626\u06C7": {
14939                 "isolated": "\uFBF0",
14940                 "final": "\uFBF1"
14941             },
14942             "\u0626\u06C6": {
14943                 "isolated": "\uFBF2",
14944                 "final": "\uFBF3"
14945             },
14946             "\u0626\u06C8": {
14947                 "isolated": "\uFBF4",
14948                 "final": "\uFBF5"
14949             },
14950             "\u0626\u06D0": {
14951                 "isolated": "\uFBF6",
14952                 "final": "\uFBF7",
14953                 "initial": "\uFBF8"
14954             },
14955             "\u0626\u0649": {
14956                 "uighur_kirghiz": {
14957                     "isolated": "\uFBF9",
14958                     "final": "\uFBFA",
14959                     "initial": "\uFBFB"
14960                 },
14961                 "isolated": "\uFC03",
14962                 "final": "\uFC68"
14963             },
14964             "\u0626\u062C": {
14965                 "isolated": "\uFC00",
14966                 "initial": "\uFC97"
14967             },
14968             "\u0626\u062D": {
14969                 "isolated": "\uFC01",
14970                 "initial": "\uFC98"
14971             },
14972             "\u0626\u0645": {
14973                 "isolated": "\uFC02",
14974                 "final": "\uFC66",
14975                 "initial": "\uFC9A",
14976                 "medial": "\uFCDF"
14977             },
14978             "\u0626\u064A": {
14979                 "isolated": "\uFC04",
14980                 "final": "\uFC69"
14981             },
14982             "\u0628\u062C": {
14983                 "isolated": "\uFC05",
14984                 "initial": "\uFC9C"
14985             },
14986             "\u0628\u062D": {
14987                 "isolated": "\uFC06",
14988                 "initial": "\uFC9D"
14989             },
14990             "\u0628\u062E": {
14991                 "isolated": "\uFC07",
14992                 "initial": "\uFC9E"
14993             },
14994             "\u0628\u0645": {
14995                 "isolated": "\uFC08",
14996                 "final": "\uFC6C",
14997                 "initial": "\uFC9F",
14998                 "medial": "\uFCE1"
14999             },
15000             "\u0628\u0649": {
15001                 "isolated": "\uFC09",
15002                 "final": "\uFC6E"
15003             },
15004             "\u0628\u064A": {
15005                 "isolated": "\uFC0A",
15006                 "final": "\uFC6F"
15007             },
15008             "\u062A\u062C": {
15009                 "isolated": "\uFC0B",
15010                 "initial": "\uFCA1"
15011             },
15012             "\u062A\u062D": {
15013                 "isolated": "\uFC0C",
15014                 "initial": "\uFCA2"
15015             },
15016             "\u062A\u062E": {
15017                 "isolated": "\uFC0D",
15018                 "initial": "\uFCA3"
15019             },
15020             "\u062A\u0645": {
15021                 "isolated": "\uFC0E",
15022                 "final": "\uFC72",
15023                 "initial": "\uFCA4",
15024                 "medial": "\uFCE3"
15025             },
15026             "\u062A\u0649": {
15027                 "isolated": "\uFC0F",
15028                 "final": "\uFC74"
15029             },
15030             "\u062A\u064A": {
15031                 "isolated": "\uFC10",
15032                 "final": "\uFC75"
15033             },
15034             "\u062B\u062C": {
15035                 "isolated": "\uFC11"
15036             },
15037             "\u062B\u0645": {
15038                 "isolated": "\uFC12",
15039                 "final": "\uFC78",
15040                 "initial": "\uFCA6",
15041                 "medial": "\uFCE5"
15042             },
15043             "\u062B\u0649": {
15044                 "isolated": "\uFC13",
15045                 "final": "\uFC7A"
15046             },
15047             "\u062B\u0648": {
15048                 "isolated": "\uFC14"
15049             },
15050             "\u062C\u062D": {
15051                 "isolated": "\uFC15",
15052                 "initial": "\uFCA7"
15053             },
15054             "\u062C\u0645": {
15055                 "isolated": "\uFC16",
15056                 "initial": "\uFCA8"
15057             },
15058             "\u062D\u062C": {
15059                 "isolated": "\uFC17",
15060                 "initial": "\uFCA9"
15061             },
15062             "\u062D\u0645": {
15063                 "isolated": "\uFC18",
15064                 "initial": "\uFCAA"
15065             },
15066             "\u062E\u062C": {
15067                 "isolated": "\uFC19",
15068                 "initial": "\uFCAB"
15069             },
15070             "\u062E\u062D": {
15071                 "isolated": "\uFC1A"
15072             },
15073             "\u062E\u0645": {
15074                 "isolated": "\uFC1B",
15075                 "initial": "\uFCAC"
15076             },
15077             "\u0633\u062C": {
15078                 "isolated": "\uFC1C",
15079                 "initial": "\uFCAD",
15080                 "medial": "\uFD34"
15081             },
15082             "\u0633\u062D": {
15083                 "isolated": "\uFC1D",
15084                 "initial": "\uFCAE",
15085                 "medial": "\uFD35"
15086             },
15087             "\u0633\u062E": {
15088                 "isolated": "\uFC1E",
15089                 "initial": "\uFCAF",
15090                 "medial": "\uFD36"
15091             },
15092             "\u0633\u0645": {
15093                 "isolated": "\uFC1F",
15094                 "initial": "\uFCB0",
15095                 "medial": "\uFCE7"
15096             },
15097             "\u0635\u062D": {
15098                 "isolated": "\uFC20",
15099                 "initial": "\uFCB1"
15100             },
15101             "\u0635\u0645": {
15102                 "isolated": "\uFC21",
15103                 "initial": "\uFCB3"
15104             },
15105             "\u0636\u062C": {
15106                 "isolated": "\uFC22",
15107                 "initial": "\uFCB4"
15108             },
15109             "\u0636\u062D": {
15110                 "isolated": "\uFC23",
15111                 "initial": "\uFCB5"
15112             },
15113             "\u0636\u062E": {
15114                 "isolated": "\uFC24",
15115                 "initial": "\uFCB6"
15116             },
15117             "\u0636\u0645": {
15118                 "isolated": "\uFC25",
15119                 "initial": "\uFCB7"
15120             },
15121             "\u0637\u062D": {
15122                 "isolated": "\uFC26",
15123                 "initial": "\uFCB8"
15124             },
15125             "\u0637\u0645": {
15126                 "isolated": "\uFC27",
15127                 "initial": "\uFD33",
15128                 "medial": "\uFD3A"
15129             },
15130             "\u0638\u0645": {
15131                 "isolated": "\uFC28",
15132                 "initial": "\uFCB9",
15133                 "medial": "\uFD3B"
15134             },
15135             "\u0639\u062C": {
15136                 "isolated": "\uFC29",
15137                 "initial": "\uFCBA"
15138             },
15139             "\u0639\u0645": {
15140                 "isolated": "\uFC2A",
15141                 "initial": "\uFCBB"
15142             },
15143             "\u063A\u062C": {
15144                 "isolated": "\uFC2B",
15145                 "initial": "\uFCBC"
15146             },
15147             "\u063A\u0645": {
15148                 "isolated": "\uFC2C",
15149                 "initial": "\uFCBD"
15150             },
15151             "\u0641\u062C": {
15152                 "isolated": "\uFC2D",
15153                 "initial": "\uFCBE"
15154             },
15155             "\u0641\u062D": {
15156                 "isolated": "\uFC2E",
15157                 "initial": "\uFCBF"
15158             },
15159             "\u0641\u062E": {
15160                 "isolated": "\uFC2F",
15161                 "initial": "\uFCC0"
15162             },
15163             "\u0641\u0645": {
15164                 "isolated": "\uFC30",
15165                 "initial": "\uFCC1"
15166             },
15167             "\u0641\u0649": {
15168                 "isolated": "\uFC31",
15169                 "final": "\uFC7C"
15170             },
15171             "\u0641\u064A": {
15172                 "isolated": "\uFC32",
15173                 "final": "\uFC7D"
15174             },
15175             "\u0642\u062D": {
15176                 "isolated": "\uFC33",
15177                 "initial": "\uFCC2"
15178             },
15179             "\u0642\u0645": {
15180                 "isolated": "\uFC34",
15181                 "initial": "\uFCC3"
15182             },
15183             "\u0642\u0649": {
15184                 "isolated": "\uFC35",
15185                 "final": "\uFC7E"
15186             },
15187             "\u0642\u064A": {
15188                 "isolated": "\uFC36",
15189                 "final": "\uFC7F"
15190             },
15191             "\u0643\u0627": {
15192                 "isolated": "\uFC37",
15193                 "final": "\uFC80"
15194             },
15195             "\u0643\u062C": {
15196                 "isolated": "\uFC38",
15197                 "initial": "\uFCC4"
15198             },
15199             "\u0643\u062D": {
15200                 "isolated": "\uFC39",
15201                 "initial": "\uFCC5"
15202             },
15203             "\u0643\u062E": {
15204                 "isolated": "\uFC3A",
15205                 "initial": "\uFCC6"
15206             },
15207             "\u0643\u0644": {
15208                 "isolated": "\uFC3B",
15209                 "final": "\uFC81",
15210                 "initial": "\uFCC7",
15211                 "medial": "\uFCEB"
15212             },
15213             "\u0643\u0645": {
15214                 "isolated": "\uFC3C",
15215                 "final": "\uFC82",
15216                 "initial": "\uFCC8",
15217                 "medial": "\uFCEC"
15218             },
15219             "\u0643\u0649": {
15220                 "isolated": "\uFC3D",
15221                 "final": "\uFC83"
15222             },
15223             "\u0643\u064A": {
15224                 "isolated": "\uFC3E",
15225                 "final": "\uFC84"
15226             },
15227             "\u0644\u062C": {
15228                 "isolated": "\uFC3F",
15229                 "initial": "\uFCC9"
15230             },
15231             "\u0644\u062D": {
15232                 "isolated": "\uFC40",
15233                 "initial": "\uFCCA"
15234             },
15235             "\u0644\u062E": {
15236                 "isolated": "\uFC41",
15237                 "initial": "\uFCCB"
15238             },
15239             "\u0644\u0645": {
15240                 "isolated": "\uFC42",
15241                 "final": "\uFC85",
15242                 "initial": "\uFCCC",
15243                 "medial": "\uFCED"
15244             },
15245             "\u0644\u0649": {
15246                 "isolated": "\uFC43",
15247                 "final": "\uFC86"
15248             },
15249             "\u0644\u064A": {
15250                 "isolated": "\uFC44",
15251                 "final": "\uFC87"
15252             },
15253             "\u0645\u062C": {
15254                 "isolated": "\uFC45",
15255                 "initial": "\uFCCE"
15256             },
15257             "\u0645\u062D": {
15258                 "isolated": "\uFC46",
15259                 "initial": "\uFCCF"
15260             },
15261             "\u0645\u062E": {
15262                 "isolated": "\uFC47",
15263                 "initial": "\uFCD0"
15264             },
15265             "\u0645\u0645": {
15266                 "isolated": "\uFC48",
15267                 "final": "\uFC89",
15268                 "initial": "\uFCD1"
15269             },
15270             "\u0645\u0649": {
15271                 "isolated": "\uFC49"
15272             },
15273             "\u0645\u064A": {
15274                 "isolated": "\uFC4A"
15275             },
15276             "\u0646\u062C": {
15277                 "isolated": "\uFC4B",
15278                 "initial": "\uFCD2"
15279             },
15280             "\u0646\u062D": {
15281                 "isolated": "\uFC4C",
15282                 "initial": "\uFCD3"
15283             },
15284             "\u0646\u062E": {
15285                 "isolated": "\uFC4D",
15286                 "initial": "\uFCD4"
15287             },
15288             "\u0646\u0645": {
15289                 "isolated": "\uFC4E",
15290                 "final": "\uFC8C",
15291                 "initial": "\uFCD5",
15292                 "medial": "\uFCEE"
15293             },
15294             "\u0646\u0649": {
15295                 "isolated": "\uFC4F",
15296                 "final": "\uFC8E"
15297             },
15298             "\u0646\u064A": {
15299                 "isolated": "\uFC50",
15300                 "final": "\uFC8F"
15301             },
15302             "\u0647\u062C": {
15303                 "isolated": "\uFC51",
15304                 "initial": "\uFCD7"
15305             },
15306             "\u0647\u0645": {
15307                 "isolated": "\uFC52",
15308                 "initial": "\uFCD8"
15309             },
15310             "\u0647\u0649": {
15311                 "isolated": "\uFC53"
15312             },
15313             "\u0647\u064A": {
15314                 "isolated": "\uFC54"
15315             },
15316             "\u064A\u062C": {
15317                 "isolated": "\uFC55",
15318                 "initial": "\uFCDA"
15319             },
15320             "\u064A\u062D": {
15321                 "isolated": "\uFC56",
15322                 "initial": "\uFCDB"
15323             },
15324             "\u064A\u062E": {
15325                 "isolated": "\uFC57",
15326                 "initial": "\uFCDC"
15327             },
15328             "\u064A\u0645": {
15329                 "isolated": "\uFC58",
15330                 "final": "\uFC93",
15331                 "initial": "\uFCDD",
15332                 "medial": "\uFCF0"
15333             },
15334             "\u064A\u0649": {
15335                 "isolated": "\uFC59",
15336                 "final": "\uFC95"
15337             },
15338             "\u064A\u064A": {
15339                 "isolated": "\uFC5A",
15340                 "final": "\uFC96"
15341             },
15342             "\u0630\u0670": {
15343                 "isolated": "\uFC5B"
15344             },
15345             "\u0631\u0670": {
15346                 "isolated": "\uFC5C"
15347             },
15348             "\u0649\u0670": {
15349                 "isolated": "\uFC5D",
15350                 "final": "\uFC90"
15351             },
15352             "\u064C\u0651": {
15353                 "isolated": "\uFC5E"
15354             },
15355             "\u064D\u0651": {
15356                 "isolated": "\uFC5F"
15357             },
15358             "\u064E\u0651": {
15359                 "isolated": "\uFC60"
15360             },
15361             "\u064F\u0651": {
15362                 "isolated": "\uFC61"
15363             },
15364             "\u0650\u0651": {
15365                 "isolated": "\uFC62"
15366             },
15367             "\u0651\u0670": {
15368                 "isolated": "\uFC63"
15369             },
15370             "\u0626\u0631": {
15371                 "final": "\uFC64"
15372             },
15373             "\u0626\u0632": {
15374                 "final": "\uFC65"
15375             },
15376             "\u0626\u0646": {
15377                 "final": "\uFC67"
15378             },
15379             "\u0628\u0631": {
15380                 "final": "\uFC6A"
15381             },
15382             "\u0628\u0632": {
15383                 "final": "\uFC6B"
15384             },
15385             "\u0628\u0646": {
15386                 "final": "\uFC6D"
15387             },
15388             "\u062A\u0631": {
15389                 "final": "\uFC70"
15390             },
15391             "\u062A\u0632": {
15392                 "final": "\uFC71"
15393             },
15394             "\u062A\u0646": {
15395                 "final": "\uFC73"
15396             },
15397             "\u062B\u0631": {
15398                 "final": "\uFC76"
15399             },
15400             "\u062B\u0632": {
15401                 "final": "\uFC77"
15402             },
15403             "\u062B\u0646": {
15404                 "final": "\uFC79"
15405             },
15406             "\u062B\u064A": {
15407                 "final": "\uFC7B"
15408             },
15409             "\u0645\u0627": {
15410                 "final": "\uFC88"
15411             },
15412             "\u0646\u0631": {
15413                 "final": "\uFC8A"
15414             },
15415             "\u0646\u0632": {
15416                 "final": "\uFC8B"
15417             },
15418             "\u0646\u0646": {
15419                 "final": "\uFC8D"
15420             },
15421             "\u064A\u0631": {
15422                 "final": "\uFC91"
15423             },
15424             "\u064A\u0632": {
15425                 "final": "\uFC92"
15426             },
15427             "\u064A\u0646": {
15428                 "final": "\uFC94"
15429             },
15430             "\u0626\u062E": {
15431                 "initial": "\uFC99"
15432             },
15433             "\u0626\u0647": {
15434                 "initial": "\uFC9B",
15435                 "medial": "\uFCE0"
15436             },
15437             "\u0628\u0647": {
15438                 "initial": "\uFCA0",
15439                 "medial": "\uFCE2"
15440             },
15441             "\u062A\u0647": {
15442                 "initial": "\uFCA5",
15443                 "medial": "\uFCE4"
15444             },
15445             "\u0635\u062E": {
15446                 "initial": "\uFCB2"
15447             },
15448             "\u0644\u0647": {
15449                 "initial": "\uFCCD"
15450             },
15451             "\u0646\u0647": {
15452                 "initial": "\uFCD6",
15453                 "medial": "\uFCEF"
15454             },
15455             "\u0647\u0670": {
15456                 "initial": "\uFCD9"
15457             },
15458             "\u064A\u0647": {
15459                 "initial": "\uFCDE",
15460                 "medial": "\uFCF1"
15461             },
15462             "\u062B\u0647": {
15463                 "medial": "\uFCE6"
15464             },
15465             "\u0633\u0647": {
15466                 "medial": "\uFCE8",
15467                 "initial": "\uFD31"
15468             },
15469             "\u0634\u0645": {
15470                 "medial": "\uFCE9",
15471                 "isolated": "\uFD0C",
15472                 "final": "\uFD28",
15473                 "initial": "\uFD30"
15474             },
15475             "\u0634\u0647": {
15476                 "medial": "\uFCEA",
15477                 "initial": "\uFD32"
15478             },
15479             "\u0640\u064E\u0651": {
15480                 "medial": "\uFCF2"
15481             },
15482             "\u0640\u064F\u0651": {
15483                 "medial": "\uFCF3"
15484             },
15485             "\u0640\u0650\u0651": {
15486                 "medial": "\uFCF4"
15487             },
15488             "\u0637\u0649": {
15489                 "isolated": "\uFCF5",
15490                 "final": "\uFD11"
15491             },
15492             "\u0637\u064A": {
15493                 "isolated": "\uFCF6",
15494                 "final": "\uFD12"
15495             },
15496             "\u0639\u0649": {
15497                 "isolated": "\uFCF7",
15498                 "final": "\uFD13"
15499             },
15500             "\u0639\u064A": {
15501                 "isolated": "\uFCF8",
15502                 "final": "\uFD14"
15503             },
15504             "\u063A\u0649": {
15505                 "isolated": "\uFCF9",
15506                 "final": "\uFD15"
15507             },
15508             "\u063A\u064A": {
15509                 "isolated": "\uFCFA",
15510                 "final": "\uFD16"
15511             },
15512             "\u0633\u0649": {
15513                 "isolated": "\uFCFB"
15514             },
15515             "\u0633\u064A": {
15516                 "isolated": "\uFCFC",
15517                 "final": "\uFD18"
15518             },
15519             "\u0634\u0649": {
15520                 "isolated": "\uFCFD",
15521                 "final": "\uFD19"
15522             },
15523             "\u0634\u064A": {
15524                 "isolated": "\uFCFE",
15525                 "final": "\uFD1A"
15526             },
15527             "\u062D\u0649": {
15528                 "isolated": "\uFCFF",
15529                 "final": "\uFD1B"
15530             },
15531             "\u062D\u064A": {
15532                 "isolated": "\uFD00",
15533                 "final": "\uFD1C"
15534             },
15535             "\u062C\u0649": {
15536                 "isolated": "\uFD01",
15537                 "final": "\uFD1D"
15538             },
15539             "\u062C\u064A": {
15540                 "isolated": "\uFD02",
15541                 "final": "\uFD1E"
15542             },
15543             "\u062E\u0649": {
15544                 "isolated": "\uFD03",
15545                 "final": "\uFD1F"
15546             },
15547             "\u062E\u064A": {
15548                 "isolated": "\uFD04",
15549                 "final": "\uFD20"
15550             },
15551             "\u0635\u0649": {
15552                 "isolated": "\uFD05",
15553                 "final": "\uFD21"
15554             },
15555             "\u0635\u064A": {
15556                 "isolated": "\uFD06",
15557                 "final": "\uFD22"
15558             },
15559             "\u0636\u0649": {
15560                 "isolated": "\uFD07",
15561                 "final": "\uFD23"
15562             },
15563             "\u0636\u064A": {
15564                 "isolated": "\uFD08",
15565                 "final": "\uFD24"
15566             },
15567             "\u0634\u062C": {
15568                 "isolated": "\uFD09",
15569                 "final": "\uFD25",
15570                 "initial": "\uFD2D",
15571                 "medial": "\uFD37"
15572             },
15573             "\u0634\u062D": {
15574                 "isolated": "\uFD0A",
15575                 "final": "\uFD26",
15576                 "initial": "\uFD2E",
15577                 "medial": "\uFD38"
15578             },
15579             "\u0634\u062E": {
15580                 "isolated": "\uFD0B",
15581                 "final": "\uFD27",
15582                 "initial": "\uFD2F",
15583                 "medial": "\uFD39"
15584             },
15585             "\u0634\u0631": {
15586                 "isolated": "\uFD0D",
15587                 "final": "\uFD29"
15588             },
15589             "\u0633\u0631": {
15590                 "isolated": "\uFD0E",
15591                 "final": "\uFD2A"
15592             },
15593             "\u0635\u0631": {
15594                 "isolated": "\uFD0F",
15595                 "final": "\uFD2B"
15596             },
15597             "\u0636\u0631": {
15598                 "isolated": "\uFD10",
15599                 "final": "\uFD2C"
15600             },
15601             "\u0633\u0639": {
15602                 "final": "\uFD17"
15603             },
15604             "\u062A\u062C\u0645": {
15605                 "initial": "\uFD50"
15606             },
15607             "\u062A\u062D\u062C": {
15608                 "final": "\uFD51",
15609                 "initial": "\uFD52"
15610             },
15611             "\u062A\u062D\u0645": {
15612                 "initial": "\uFD53"
15613             },
15614             "\u062A\u062E\u0645": {
15615                 "initial": "\uFD54"
15616             },
15617             "\u062A\u0645\u062C": {
15618                 "initial": "\uFD55"
15619             },
15620             "\u062A\u0645\u062D": {
15621                 "initial": "\uFD56"
15622             },
15623             "\u062A\u0645\u062E": {
15624                 "initial": "\uFD57"
15625             },
15626             "\u062C\u0645\u062D": {
15627                 "final": "\uFD58",
15628                 "initial": "\uFD59"
15629             },
15630             "\u062D\u0645\u064A": {
15631                 "final": "\uFD5A"
15632             },
15633             "\u062D\u0645\u0649": {
15634                 "final": "\uFD5B"
15635             },
15636             "\u0633\u062D\u062C": {
15637                 "initial": "\uFD5C"
15638             },
15639             "\u0633\u062C\u062D": {
15640                 "initial": "\uFD5D"
15641             },
15642             "\u0633\u062C\u0649": {
15643                 "final": "\uFD5E"
15644             },
15645             "\u0633\u0645\u062D": {
15646                 "final": "\uFD5F",
15647                 "initial": "\uFD60"
15648             },
15649             "\u0633\u0645\u062C": {
15650                 "initial": "\uFD61"
15651             },
15652             "\u0633\u0645\u0645": {
15653                 "final": "\uFD62",
15654                 "initial": "\uFD63"
15655             },
15656             "\u0635\u062D\u062D": {
15657                 "final": "\uFD64",
15658                 "initial": "\uFD65"
15659             },
15660             "\u0635\u0645\u0645": {
15661                 "final": "\uFD66",
15662                 "initial": "\uFDC5"
15663             },
15664             "\u0634\u062D\u0645": {
15665                 "final": "\uFD67",
15666                 "initial": "\uFD68"
15667             },
15668             "\u0634\u062C\u064A": {
15669                 "final": "\uFD69"
15670             },
15671             "\u0634\u0645\u062E": {
15672                 "final": "\uFD6A",
15673                 "initial": "\uFD6B"
15674             },
15675             "\u0634\u0645\u0645": {
15676                 "final": "\uFD6C",
15677                 "initial": "\uFD6D"
15678             },
15679             "\u0636\u062D\u0649": {
15680                 "final": "\uFD6E"
15681             },
15682             "\u0636\u062E\u0645": {
15683                 "final": "\uFD6F",
15684                 "initial": "\uFD70"
15685             },
15686             "\u0636\u0645\u062D": {
15687                 "final": "\uFD71"
15688             },
15689             "\u0637\u0645\u062D": {
15690                 "initial": "\uFD72"
15691             },
15692             "\u0637\u0645\u0645": {
15693                 "initial": "\uFD73"
15694             },
15695             "\u0637\u0645\u064A": {
15696                 "final": "\uFD74"
15697             },
15698             "\u0639\u062C\u0645": {
15699                 "final": "\uFD75",
15700                 "initial": "\uFDC4"
15701             },
15702             "\u0639\u0645\u0645": {
15703                 "final": "\uFD76",
15704                 "initial": "\uFD77"
15705             },
15706             "\u0639\u0645\u0649": {
15707                 "final": "\uFD78"
15708             },
15709             "\u063A\u0645\u0645": {
15710                 "final": "\uFD79"
15711             },
15712             "\u063A\u0645\u064A": {
15713                 "final": "\uFD7A"
15714             },
15715             "\u063A\u0645\u0649": {
15716                 "final": "\uFD7B"
15717             },
15718             "\u0641\u062E\u0645": {
15719                 "final": "\uFD7C",
15720                 "initial": "\uFD7D"
15721             },
15722             "\u0642\u0645\u062D": {
15723                 "final": "\uFD7E",
15724                 "initial": "\uFDB4"
15725             },
15726             "\u0642\u0645\u0645": {
15727                 "final": "\uFD7F"
15728             },
15729             "\u0644\u062D\u0645": {
15730                 "final": "\uFD80",
15731                 "initial": "\uFDB5"
15732             },
15733             "\u0644\u062D\u064A": {
15734                 "final": "\uFD81"
15735             },
15736             "\u0644\u062D\u0649": {
15737                 "final": "\uFD82"
15738             },
15739             "\u0644\u062C\u062C": {
15740                 "initial": "\uFD83",
15741                 "final": "\uFD84"
15742             },
15743             "\u0644\u062E\u0645": {
15744                 "final": "\uFD85",
15745                 "initial": "\uFD86"
15746             },
15747             "\u0644\u0645\u062D": {
15748                 "final": "\uFD87",
15749                 "initial": "\uFD88"
15750             },
15751             "\u0645\u062D\u062C": {
15752                 "initial": "\uFD89"
15753             },
15754             "\u0645\u062D\u0645": {
15755                 "initial": "\uFD8A"
15756             },
15757             "\u0645\u062D\u064A": {
15758                 "final": "\uFD8B"
15759             },
15760             "\u0645\u062C\u062D": {
15761                 "initial": "\uFD8C"
15762             },
15763             "\u0645\u062C\u0645": {
15764                 "initial": "\uFD8D"
15765             },
15766             "\u0645\u062E\u062C": {
15767                 "initial": "\uFD8E"
15768             },
15769             "\u0645\u062E\u0645": {
15770                 "initial": "\uFD8F"
15771             },
15772             "\u0645\u062C\u062E": {
15773                 "initial": "\uFD92"
15774             },
15775             "\u0647\u0645\u062C": {
15776                 "initial": "\uFD93"
15777             },
15778             "\u0647\u0645\u0645": {
15779                 "initial": "\uFD94"
15780             },
15781             "\u0646\u062D\u0645": {
15782                 "initial": "\uFD95"
15783             },
15784             "\u0646\u062D\u0649": {
15785                 "final": "\uFD96"
15786             },
15787             "\u0646\u062C\u0645": {
15788                 "final": "\uFD97",
15789                 "initial": "\uFD98"
15790             },
15791             "\u0646\u062C\u0649": {
15792                 "final": "\uFD99"
15793             },
15794             "\u0646\u0645\u064A": {
15795                 "final": "\uFD9A"
15796             },
15797             "\u0646\u0645\u0649": {
15798                 "final": "\uFD9B"
15799             },
15800             "\u064A\u0645\u0645": {
15801                 "final": "\uFD9C",
15802                 "initial": "\uFD9D"
15803             },
15804             "\u0628\u062E\u064A": {
15805                 "final": "\uFD9E"
15806             },
15807             "\u062A\u062C\u064A": {
15808                 "final": "\uFD9F"
15809             },
15810             "\u062A\u062C\u0649": {
15811                 "final": "\uFDA0"
15812             },
15813             "\u062A\u062E\u064A": {
15814                 "final": "\uFDA1"
15815             },
15816             "\u062A\u062E\u0649": {
15817                 "final": "\uFDA2"
15818             },
15819             "\u062A\u0645\u064A": {
15820                 "final": "\uFDA3"
15821             },
15822             "\u062A\u0645\u0649": {
15823                 "final": "\uFDA4"
15824             },
15825             "\u062C\u0645\u064A": {
15826                 "final": "\uFDA5"
15827             },
15828             "\u062C\u062D\u0649": {
15829                 "final": "\uFDA6"
15830             },
15831             "\u062C\u0645\u0649": {
15832                 "final": "\uFDA7"
15833             },
15834             "\u0633\u062E\u0649": {
15835                 "final": "\uFDA8"
15836             },
15837             "\u0635\u062D\u064A": {
15838                 "final": "\uFDA9"
15839             },
15840             "\u0634\u062D\u064A": {
15841                 "final": "\uFDAA"
15842             },
15843             "\u0636\u062D\u064A": {
15844                 "final": "\uFDAB"
15845             },
15846             "\u0644\u062C\u064A": {
15847                 "final": "\uFDAC"
15848             },
15849             "\u0644\u0645\u064A": {
15850                 "final": "\uFDAD"
15851             },
15852             "\u064A\u062D\u064A": {
15853                 "final": "\uFDAE"
15854             },
15855             "\u064A\u062C\u064A": {
15856                 "final": "\uFDAF"
15857             },
15858             "\u064A\u0645\u064A": {
15859                 "final": "\uFDB0"
15860             },
15861             "\u0645\u0645\u064A": {
15862                 "final": "\uFDB1"
15863             },
15864             "\u0642\u0645\u064A": {
15865                 "final": "\uFDB2"
15866             },
15867             "\u0646\u062D\u064A": {
15868                 "final": "\uFDB3"
15869             },
15870             "\u0639\u0645\u064A": {
15871                 "final": "\uFDB6"
15872             },
15873             "\u0643\u0645\u064A": {
15874                 "final": "\uFDB7"
15875             },
15876             "\u0646\u062C\u062D": {
15877                 "initial": "\uFDB8",
15878                 "final": "\uFDBD"
15879             },
15880             "\u0645\u062E\u064A": {
15881                 "final": "\uFDB9"
15882             },
15883             "\u0644\u062C\u0645": {
15884                 "initial": "\uFDBA",
15885                 "final": "\uFDBC"
15886             },
15887             "\u0643\u0645\u0645": {
15888                 "final": "\uFDBB",
15889                 "initial": "\uFDC3"
15890             },
15891             "\u062C\u062D\u064A": {
15892                 "final": "\uFDBE"
15893             },
15894             "\u062D\u062C\u064A": {
15895                 "final": "\uFDBF"
15896             },
15897             "\u0645\u062C\u064A": {
15898                 "final": "\uFDC0"
15899             },
15900             "\u0641\u0645\u064A": {
15901                 "final": "\uFDC1"
15902             },
15903             "\u0628\u062D\u064A": {
15904                 "final": "\uFDC2"
15905             },
15906             "\u0633\u062E\u064A": {
15907                 "final": "\uFDC6"
15908             },
15909             "\u0646\u062C\u064A": {
15910                 "final": "\uFDC7"
15911             },
15912             "\u0644\u0622": {
15913                 "isolated": "\uFEF5",
15914                 "final": "\uFEF6"
15915             },
15916             "\u0644\u0623": {
15917                 "isolated": "\uFEF7",
15918                 "final": "\uFEF8"
15919             },
15920             "\u0644\u0625": {
15921                 "isolated": "\uFEF9",
15922                 "final": "\uFEFA"
15923             },
15924             "\u0644\u0627": {
15925                 "isolated": "\uFEFB",
15926                 "final": "\uFEFC"
15927             },
15928             "words": {
15929                 "\u0635\u0644\u06D2": "\uFDF0",
15930                 "\u0642\u0644\u06D2": "\uFDF1",
15931                 "\u0627\u0644\u0644\u0647": "\uFDF2",
15932                 "\u0627\u0643\u0628\u0631": "\uFDF3",
15933                 "\u0645\u062D\u0645\u062F": "\uFDF4",
15934                 "\u0635\u0644\u0639\u0645": "\uFDF5",
15935                 "\u0631\u0633\u0648\u0644": "\uFDF6",
15936                 "\u0639\u0644\u064A\u0647": "\uFDF7",
15937                 "\u0648\u0633\u0644\u0645": "\uFDF8",
15938                 "\u0635\u0644\u0649": "\uFDF9",
15939                 "\u0635\u0644\u0649\u0627\u0644\u0644\u0647\u0639\u0644\u064A\u0647\u0648\u0633\u0644\u0645": "\uFDFA",
15940                 "\u062C\u0644\u062C\u0644\u0627\u0644\u0647": "\uFDFB",
15941                 "\u0631\u06CC\u0627\u0644": "\uFDFC"
15942             }
15943         };
15944         exports.default = ligatureReference;
15945         });
15946
15947         var reference = createCommonjsModule(function (module, exports) {
15948         Object.defineProperty(exports, "__esModule", { value: true });
15949
15950
15951         var letterList = Object.keys(unicodeArabic.default);
15952         exports.letterList = letterList;
15953         var ligatureList = Object.keys(unicodeLigatures.default);
15954         exports.ligatureList = ligatureList;
15955         var ligatureWordList = Object.keys(unicodeLigatures.default.words);
15956         exports.ligatureWordList = ligatureWordList;
15957         var lams = '\u0644\u06B5\u06B6\u06B7\u06B8';
15958         exports.lams = lams;
15959         var alefs = '\u0627\u0622\u0623\u0625\u0671\u0672\u0673\u0675\u0773\u0774';
15960         exports.alefs = alefs;
15961         // for (var l = 1; l < lams.length; l++) {
15962         //   console.log('-');
15963         //   for (var a = 0; a < alefs.length; a++) {
15964         //     console.log(a + ': ' + lams[l] + alefs[a]);
15965         //   }
15966         // }
15967         var tashkeel = '\u0605\u0640\u0670\u0674\u06DF\u06E7\u06E8';
15968         exports.tashkeel = tashkeel;
15969         function addToTashkeel(start, finish) {
15970             for (var i = start; i <= finish; i++) {
15971                 exports.tashkeel = tashkeel += String.fromCharCode(i);
15972             }
15973         }
15974         addToTashkeel(0x0610, 0x061A);
15975         addToTashkeel(0x064B, 0x065F);
15976         addToTashkeel(0x06D6, 0x06DC);
15977         addToTashkeel(0x06E0, 0x06E4);
15978         addToTashkeel(0x06EA, 0x06ED);
15979         addToTashkeel(0x08D3, 0x08E1);
15980         addToTashkeel(0x08E3, 0x08FF);
15981         addToTashkeel(0xFE70, 0xFE7F);
15982         var lineBreakers = '\u0627\u0629\u0648\u06C0\u06CF\u06FD\u06FE\u076B\u076C\u0771\u0773\u0774\u0778\u0779\u08E2\u08B1\u08B2\u08B9';
15983         exports.lineBreakers = lineBreakers;
15984         function addToLineBreakers(start, finish) {
15985             for (var i = start; i <= finish; i++) {
15986                 exports.lineBreakers = lineBreakers += String.fromCharCode(i);
15987             }
15988         }
15989         addToLineBreakers(0x0600, 0x061F); // it's OK to include tashkeel in this range as it is ignored
15990         addToLineBreakers(0x0621, 0x0625);
15991         addToLineBreakers(0x062F, 0x0632);
15992         addToLineBreakers(0x0660, 0x066D); // numerals, math
15993         addToLineBreakers(0x0671, 0x0677);
15994         addToLineBreakers(0x0688, 0x0699);
15995         addToLineBreakers(0x06C3, 0x06CB);
15996         addToLineBreakers(0x06D2, 0x06F9);
15997         addToLineBreakers(0x0759, 0x075B);
15998         addToLineBreakers(0x08AA, 0x08AE);
15999         addToLineBreakers(0xFB50, 0xFDFD); // presentation forms look like they could connect, but never do
16000         // Presentation Forms A includes diacritics but they are meant to stand alone
16001         addToLineBreakers(0xFE80, 0xFEFC); // presentation forms look like they could connect, but never do
16002         // numerals, math
16003         addToLineBreakers(0x10E60, 0x10E7F);
16004         addToLineBreakers(0x1EC70, 0x1ECBF);
16005         addToLineBreakers(0x1EE00, 0x1EEFF);
16006         });
16007
16008         var GlyphSplitter_1 = createCommonjsModule(function (module, exports) {
16009         Object.defineProperty(exports, "__esModule", { value: true });
16010
16011
16012         function GlyphSplitter(word) {
16013             var letters = [];
16014             var lastLetter = '';
16015             word.split('').forEach(function (letter) {
16016                 if (isArabic_1.isArabic(letter)) {
16017                     if (reference.tashkeel.indexOf(letter) > -1) {
16018                         letters[letters.length - 1] += letter;
16019                     }
16020                     else if (lastLetter.length && ((reference.lams.indexOf(lastLetter) === 0 && reference.alefs.indexOf(letter) > -1) || (reference.lams.indexOf(lastLetter) > 0 && reference.alefs.indexOf(letter) === 0))) {
16021                         // valid LA forms
16022                         letters[letters.length - 1] += letter;
16023                     }
16024                     else {
16025                         letters.push(letter);
16026                     }
16027                 }
16028                 else {
16029                     letters.push(letter);
16030                 }
16031                 if (reference.tashkeel.indexOf(letter) === -1) {
16032                     lastLetter = letter;
16033                 }
16034             });
16035             return letters;
16036         }
16037         exports.GlyphSplitter = GlyphSplitter;
16038         });
16039
16040         var BaselineSplitter_1 = createCommonjsModule(function (module, exports) {
16041         Object.defineProperty(exports, "__esModule", { value: true });
16042
16043
16044         function BaselineSplitter(word) {
16045             var letters = [];
16046             var lastLetter = '';
16047             word.split('').forEach(function (letter) {
16048                 if (isArabic_1.isArabic(letter) && isArabic_1.isArabic(lastLetter)) {
16049                     if (lastLetter.length && reference.tashkeel.indexOf(letter) > -1) {
16050                         letters[letters.length - 1] += letter;
16051                     }
16052                     else if (reference.lineBreakers.indexOf(lastLetter) > -1) {
16053                         letters.push(letter);
16054                     }
16055                     else {
16056                         letters[letters.length - 1] += letter;
16057                     }
16058                 }
16059                 else {
16060                     letters.push(letter);
16061                 }
16062                 if (reference.tashkeel.indexOf(letter) === -1) {
16063                     // don't allow tashkeel to hide line break
16064                     lastLetter = letter;
16065                 }
16066             });
16067             return letters;
16068         }
16069         exports.BaselineSplitter = BaselineSplitter;
16070         });
16071
16072         var Normalization = createCommonjsModule(function (module, exports) {
16073         Object.defineProperty(exports, "__esModule", { value: true });
16074
16075
16076
16077
16078         function Normal(word, breakPresentationForm) {
16079             // default is to turn initial/isolated/medial/final presentation form to generic
16080             if (typeof breakPresentationForm === 'undefined') {
16081                 breakPresentationForm = true;
16082             }
16083             var returnable = '';
16084             word.split('').forEach(function (letter) {
16085                 if (!isArabic_1.isArabic(letter)) {
16086                     returnable += letter;
16087                     return;
16088                 }
16089                 for (var w = 0; w < reference.letterList.length; w++) {
16090                     // ok so we are checking this potential lettertron
16091                     var letterForms = unicodeArabic.default[reference.letterList[w]];
16092                     var versions = Object.keys(letterForms);
16093                     for (var v = 0; v < versions.length; v++) {
16094                         var localVersion = letterForms[versions[v]];
16095                         if (typeof localVersion === 'object' && typeof localVersion.indexOf === 'undefined') {
16096                             // look at this embedded object
16097                             var embeddedForms = Object.keys(localVersion);
16098                             for (var ef = 0; ef < embeddedForms.length; ef++) {
16099                                 var form = localVersion[embeddedForms[ef]];
16100                                 if (form === letter || (typeof form === 'object' && form.indexOf && form.indexOf(letter) > -1)) {
16101                                     // match
16102                                     // console.log('embedded match');
16103                                     if (form === letter) {
16104                                         // match exact
16105                                         if (breakPresentationForm && localVersion['normal'] && ['isolated', 'initial', 'medial', 'final'].indexOf(embeddedForms[ef]) > -1) {
16106                                             // replace presentation form
16107                                             // console.log('keeping normal form of the letter');
16108                                             if (typeof localVersion['normal'] === 'object') {
16109                                                 returnable += localVersion['normal'][0];
16110                                             }
16111                                             else {
16112                                                 returnable += localVersion['normal'];
16113                                             }
16114                                             return;
16115                                         }
16116                                         // console.log('keeping this letter');
16117                                         returnable += letter;
16118                                         return;
16119                                     }
16120                                     else if (typeof form === 'object' && form.indexOf && form.indexOf(letter) > -1) {
16121                                         // match
16122                                         returnable += form[0];
16123                                         // console.log('added the first letter from the same array');
16124                                         return;
16125                                     }
16126                                 }
16127                             }
16128                         }
16129                         else if (localVersion === letter) {
16130                             // match exact
16131                             if (breakPresentationForm && letterForms['normal'] && ['isolated', 'initial', 'medial', 'final'].indexOf(versions[v]) > -1) {
16132                                 // replace presentation form
16133                                 // console.log('keeping normal form of the letter');
16134                                 if (typeof letterForms['normal'] === 'object') {
16135                                     returnable += letterForms['normal'][0];
16136                                 }
16137                                 else {
16138                                     returnable += letterForms['normal'];
16139                                 }
16140                                 return;
16141                             }
16142                             // console.log('keeping this letter');
16143                             returnable += letter;
16144                             return;
16145                         }
16146                         else if (typeof localVersion === 'object' && localVersion.indexOf && localVersion.indexOf(letter) > -1) {
16147                             // match
16148                             returnable += localVersion[0];
16149                             // console.log('added the first letter from the same array');
16150                             return;
16151                         }
16152                     }
16153                 }
16154                 // try ligatures
16155                 for (var v2 = 0; v2 < reference.ligatureList.length; v2++) {
16156                     var normalForm = reference.ligatureList[v2];
16157                     if (normalForm !== 'words') {
16158                         var ligForms = Object.keys(unicodeLigatures.default[normalForm]);
16159                         for (var f = 0; f < ligForms.length; f++) {
16160                             if (unicodeLigatures.default[normalForm][ligForms[f]] === letter) {
16161                                 returnable += normalForm;
16162                                 return;
16163                             }
16164                         }
16165                     }
16166                 }
16167                 // try words ligatures
16168                 for (var v3 = 0; v3 < reference.ligatureWordList.length; v3++) {
16169                     var normalForm$1 = reference.ligatureWordList[v3];
16170                     if (unicodeLigatures.default.words[normalForm$1] === letter) {
16171                         returnable += normalForm$1;
16172                         return;
16173                     }
16174                 }
16175                 returnable += letter;
16176                 // console.log('kept the letter')
16177             });
16178             return returnable;
16179         }
16180         exports.Normal = Normal;
16181         });
16182
16183         var CharShaper_1 = createCommonjsModule(function (module, exports) {
16184         Object.defineProperty(exports, "__esModule", { value: true });
16185
16186
16187
16188         function CharShaper(letter, form) {
16189             if (!isArabic_1.isArabic(letter)) {
16190                 // fail not Arabic
16191                 throw new Error('Not Arabic');
16192             }
16193             if (letter === "\u0621") {
16194                 // hamza alone
16195                 return "\u0621";
16196             }
16197             for (var w = 0; w < reference.letterList.length; w++) {
16198                 // ok so we are checking this potential lettertron
16199                 var letterForms = unicodeArabic.default[reference.letterList[w]];
16200                 var versions = Object.keys(letterForms);
16201                 for (var v = 0; v < versions.length; v++) {
16202                     var localVersion = letterForms[versions[v]];
16203                     if ((localVersion === letter) ||
16204                         (typeof localVersion === 'object' && localVersion.indexOf && localVersion.indexOf(letter) > -1)) {
16205                         if (versions.indexOf(form) > -1) {
16206                             return letterForms[form];
16207                         }
16208                     }
16209                     else if (typeof localVersion === 'object' && typeof localVersion.indexOf === 'undefined') {
16210                         // check embedded
16211                         var embeddedVersions = Object.keys(localVersion);
16212                         for (var ev = 0; ev < embeddedVersions.length; ev++) {
16213                             if ((localVersion[embeddedVersions[ev]] === letter) ||
16214                                 (typeof localVersion[embeddedVersions[ev]] === 'object' && localVersion[embeddedVersions[ev]].indexOf && localVersion[embeddedVersions[ev]].indexOf(letter) > -1)) {
16215                                 if (embeddedVersions.indexOf(form) > -1) {
16216                                     return localVersion[form];
16217                                 }
16218                             }
16219                         }
16220                     }
16221                 }
16222             }
16223         }
16224         exports.CharShaper = CharShaper;
16225         });
16226
16227         var WordShaper_1 = createCommonjsModule(function (module, exports) {
16228         Object.defineProperty(exports, "__esModule", { value: true });
16229
16230
16231
16232
16233         function WordShaper(word) {
16234             var state = 'initial';
16235             var output = '';
16236             for (var w = 0; w < word.length; w++) {
16237                 var nextLetter = ' ';
16238                 for (var nxw = w + 1; nxw < word.length; nxw++) {
16239                     if (!isArabic_1.isArabic(word[nxw])) {
16240                         break;
16241                     }
16242                     if (reference.tashkeel.indexOf(word[nxw]) === -1) {
16243                         nextLetter = word[nxw];
16244                         break;
16245                     }
16246                 }
16247                 if (!isArabic_1.isArabic(word[w]) || isArabic_1.isMath(word[w])) {
16248                     // space or other non-Arabic
16249                     output += word[w];
16250                     state = 'initial';
16251                 }
16252                 else if (reference.tashkeel.indexOf(word[w]) > -1) {
16253                     // tashkeel - add without changing state
16254                     output += word[w];
16255                 }
16256                 else if ((nextLetter === ' ') // last Arabic letter in this word
16257                     || (reference.lineBreakers.indexOf(word[w]) > -1)) { // the current letter is known to break lines
16258                     output += CharShaper_1.CharShaper(word[w], state === 'initial' ? 'isolated' : 'final');
16259                     state = 'initial';
16260                 }
16261                 else if (reference.lams.indexOf(word[w]) > -1 && reference.alefs.indexOf(nextLetter) > -1) {
16262                     // LA letters - advance an additional letter after this
16263                     output += unicodeLigatures.default[word[w] + nextLetter][(state === 'initial' ? 'isolated' : 'final')];
16264                     while (word[w] !== nextLetter) {
16265                         w++;
16266                     }
16267                     state = 'initial';
16268                 }
16269                 else {
16270                     output += CharShaper_1.CharShaper(word[w], state);
16271                     state = 'medial';
16272                 }
16273             }
16274             return output;
16275         }
16276         exports.WordShaper = WordShaper;
16277         });
16278
16279         var ParentLetter_1 = createCommonjsModule(function (module, exports) {
16280         Object.defineProperty(exports, "__esModule", { value: true });
16281
16282
16283
16284         function ParentLetter(letter) {
16285             if (!isArabic_1.isArabic(letter)) {
16286                 throw new Error('Not an Arabic letter');
16287             }
16288             for (var w = 0; w < reference.letterList.length; w++) {
16289                 // ok so we are checking this potential lettertron
16290                 var letterForms = unicodeArabic.default[reference.letterList[w]];
16291                 var versions = Object.keys(letterForms);
16292                 for (var v = 0; v < versions.length; v++) {
16293                     var localVersion = letterForms[versions[v]];
16294                     if (typeof localVersion === 'object' && typeof localVersion.indexOf === 'undefined') {
16295                         // look at this embedded object
16296                         var embeddedForms = Object.keys(localVersion);
16297                         for (var ef = 0; ef < embeddedForms.length; ef++) {
16298                             var form = localVersion[embeddedForms[ef]];
16299                             if (form === letter || (typeof form === 'object' && form.indexOf && form.indexOf(letter) > -1)) {
16300                                 // match
16301                                 return localVersion;
16302                             }
16303                         }
16304                     }
16305                     else if (localVersion === letter || (typeof localVersion === 'object' && localVersion.indexOf && localVersion.indexOf(letter) > -1)) {
16306                         // match
16307                         return letterForms;
16308                     }
16309                 }
16310                 return null;
16311             }
16312         }
16313         exports.ParentLetter = ParentLetter;
16314         function GrandparentLetter(letter) {
16315             if (!isArabic_1.isArabic(letter)) {
16316                 throw new Error('Not an Arabic letter');
16317             }
16318             for (var w = 0; w < reference.letterList.length; w++) {
16319                 // ok so we are checking this potential lettertron
16320                 var letterForms = unicodeArabic.default[reference.letterList[w]];
16321                 var versions = Object.keys(letterForms);
16322                 for (var v = 0; v < versions.length; v++) {
16323                     var localVersion = letterForms[versions[v]];
16324                     if (typeof localVersion === 'object' && typeof localVersion.indexOf === 'undefined') {
16325                         // look at this embedded object
16326                         var embeddedForms = Object.keys(localVersion);
16327                         for (var ef = 0; ef < embeddedForms.length; ef++) {
16328                             var form = localVersion[embeddedForms[ef]];
16329                             if (form === letter || (typeof form === 'object' && form.indexOf && form.indexOf(letter) > -1)) {
16330                                 // match
16331                                 return letterForms;
16332                             }
16333                         }
16334                     }
16335                     else if (localVersion === letter || (typeof localVersion === 'object' && localVersion.indexOf && localVersion.indexOf(letter) > -1)) {
16336                         // match
16337                         return letterForms;
16338                     }
16339                 }
16340                 return null;
16341             }
16342         }
16343         exports.GrandparentLetter = GrandparentLetter;
16344         });
16345
16346         var lib$1 = createCommonjsModule(function (module, exports) {
16347         Object.defineProperty(exports, "__esModule", { value: true });
16348
16349         exports.isArabic = isArabic_1.isArabic;
16350
16351         exports.GlyphSplitter = GlyphSplitter_1.GlyphSplitter;
16352
16353         exports.BaselineSplitter = BaselineSplitter_1.BaselineSplitter;
16354
16355         exports.Normal = Normalization.Normal;
16356
16357         exports.CharShaper = CharShaper_1.CharShaper;
16358
16359         exports.WordShaper = WordShaper_1.WordShaper;
16360
16361         exports.ParentLetter = ParentLetter_1.ParentLetter;
16362         exports.GrandparentLetter = ParentLetter_1.GrandparentLetter;
16363         });
16364
16365         // see https://github.com/openstreetmap/iD/pull/3707
16366
16367         var rtlRegex = /[\u0590-\u05FF\u0600-\u06FF\u0750-\u07BF\u08A0–\u08BF]/;
16368
16369         function fixRTLTextForSvg(inputText) {
16370             var ret = '', rtlBuffer = [];
16371             var arabicRegex = /[\u0600-\u06FF]/g;
16372             var arabicDiacritics = /[\u0610-\u061A\u064B-\u065F\u0670\u06D6-\u06ED]/g;
16373             var arabicMath = /[\u0660-\u066C\u06F0-\u06F9]+/g;
16374             var thaanaVowel = /[\u07A6-\u07B0]/;
16375             var hebrewSign = /[\u0591-\u05bd\u05bf\u05c1-\u05c5\u05c7]/;
16376
16377             // Arabic word shaping
16378             if (arabicRegex.test(inputText)) {
16379                 inputText = lib$1.WordShaper(inputText);
16380             }
16381
16382             for (var n = 0; n < inputText.length; n++) {
16383                 var c = inputText[n];
16384                 if (arabicMath.test(c)) {
16385                     // Arabic numbers go LTR
16386                     ret += rtlBuffer.reverse().join('');
16387                     rtlBuffer = [c];
16388                 } else {
16389                     if (rtlBuffer.length && arabicMath.test(rtlBuffer[rtlBuffer.length - 1])) {
16390                         ret += rtlBuffer.reverse().join('');
16391                         rtlBuffer = [];
16392                     }
16393                     if ((thaanaVowel.test(c) || hebrewSign.test(c) || arabicDiacritics.test(c)) && rtlBuffer.length) {
16394                         rtlBuffer[rtlBuffer.length - 1] += c;
16395                     } else if (rtlRegex.test(c)
16396                         // include Arabic presentation forms
16397                         || (c.charCodeAt(0) >= 64336 && c.charCodeAt(0) <= 65023)
16398                         || (c.charCodeAt(0) >= 65136 && c.charCodeAt(0) <= 65279)) {
16399                         rtlBuffer.push(c);
16400                     } else if (c === ' ' && rtlBuffer.length) {
16401                         // whitespace within RTL text
16402                         rtlBuffer = [rtlBuffer.reverse().join('') + ' '];
16403                     } else {
16404                         // non-RTL character
16405                         ret += rtlBuffer.reverse().join('') + c;
16406                         rtlBuffer = [];
16407                     }
16408                 }
16409             }
16410             ret += rtlBuffer.reverse().join('');
16411             return ret;
16412         }
16413
16414         // https://github.com/openstreetmap/iD/issues/772
16415         // http://mathiasbynens.be/notes/localstorage-pattern#comment-9
16416         var _storage;
16417         try { _storage = localStorage; } catch (e) {}  // eslint-disable-line no-empty
16418         _storage = _storage || (function () {
16419           var s = {};
16420           return {
16421             getItem: function (k) { return s[k]; },
16422             setItem: function (k, v) { return s[k] = v; },
16423             removeItem: function (k) { return delete s[k]; }
16424           };
16425         })();
16426
16427         //
16428         // corePreferences is an interface for persisting basic key-value strings
16429         // within and between iD sessions on the same site.
16430         //
16431         function corePreferences(k, v) {
16432
16433           try {
16434             if (arguments.length === 1) { return _storage.getItem(k); }
16435             else if (v === null) { _storage.removeItem(k); }
16436             else { _storage.setItem(k, v); }
16437           } catch (e) {
16438             /* eslint-disable no-console */
16439             if (typeof console !== 'undefined') {
16440               console.error('localStorage quota exceeded');
16441             }
16442             /* eslint-enable no-console */
16443           }
16444
16445         }
16446
16447         function responseText(response) {
16448           if (!response.ok) { throw new Error(response.status + " " + response.statusText); }
16449           return response.text();
16450         }
16451
16452         function d3_text(input, init) {
16453           return fetch(input, init).then(responseText);
16454         }
16455
16456         function responseJson(response) {
16457           if (!response.ok) { throw new Error(response.status + " " + response.statusText); }
16458           if (response.status === 204 || response.status === 205) { return; }
16459           return response.json();
16460         }
16461
16462         function d3_json(input, init) {
16463           return fetch(input, init).then(responseJson);
16464         }
16465
16466         function parser(type) {
16467           return function(input, init)  {
16468             return d3_text(input, init).then(function(text) {
16469               return (new DOMParser).parseFromString(text, type);
16470             });
16471           };
16472         }
16473
16474         var d3_xml = parser("application/xml");
16475
16476         var svg = parser("image/svg+xml");
16477
16478         var _mainFileFetcher = coreFileFetcher(); // singleton
16479
16480         //
16481         // coreFileFetcher asynchronously fetches data from JSON files
16482         //
16483         function coreFileFetcher() {
16484           var _this = {};
16485           var _inflight = {};
16486           var _fileMap = {
16487             'address_formats': 'data/address_formats.min.json',
16488             'deprecated': 'data/deprecated.min.json',
16489             'discarded': 'data/discarded.min.json',
16490             'imagery': 'data/imagery.min.json',
16491             'intro_graph': 'data/intro_graph.min.json',
16492             'keepRight': 'data/keepRight.min.json',
16493             'languages': 'data/languages.min.json',
16494             'locales': 'data/locales.min.json',
16495             'nsi_brands': 'https://cdn.jsdelivr.net/npm/name-suggestion-index@4/dist/brands.min.json',
16496             'nsi_filters': 'https://cdn.jsdelivr.net/npm/name-suggestion-index@4/dist/filters.min.json',
16497             'oci_features': 'https://cdn.jsdelivr.net/npm/osm-community-index@2/dist/features.min.json',
16498             'oci_resources': 'https://cdn.jsdelivr.net/npm/osm-community-index@2/dist/resources.min.json',
16499             'preset_categories': 'data/preset_categories.min.json',
16500             'preset_defaults': 'data/preset_defaults.min.json',
16501             'preset_fields': 'data/preset_fields.min.json',
16502             'preset_presets': 'data/preset_presets.min.json',
16503             'phone_formats': 'data/phone_formats.min.json',
16504             'qa_data': 'data/qa_data.min.json',
16505             'shortcuts': 'data/shortcuts.min.json',
16506             'territory_languages': 'data/territory_languages.min.json',
16507             'wmf_sitematrix': 'https://cdn.jsdelivr.net/npm/wmf-sitematrix@0.1/wikipedia.min.json'
16508           };
16509
16510           var _cachedData = {};
16511           // expose the cache; useful for tests
16512           _this.cache = function () { return _cachedData; };
16513
16514
16515           // Returns a Promise to fetch data
16516           // (resolved with the data if we have it already)
16517           _this.get = function (which) {
16518             if (_cachedData[which]) {
16519               return Promise.resolve(_cachedData[which]);
16520             }
16521
16522             var file = _fileMap[which];
16523             var url = file && _this.asset(file);
16524             if (!url) {
16525               return Promise.reject(("Unknown data file for \"" + which + "\""));
16526             }
16527
16528             var prom = _inflight[url];
16529             if (!prom) {
16530               _inflight[url] = prom = d3_json(url)
16531                 .then(function (result) {
16532                   delete _inflight[url];
16533                   if (!result) {
16534                     throw new Error(("No data loaded for \"" + which + "\""));
16535                   }
16536                   _cachedData[which] = result;
16537                   return result;
16538                 })
16539                 .catch(function (err) {
16540                   delete _inflight[url];
16541                   throw err;
16542                 });
16543             }
16544
16545             return prom;
16546           };
16547
16548
16549           // Accessor for the file map
16550           _this.fileMap = function(val) {
16551             if (!arguments.length) { return _fileMap; }
16552             _fileMap = val;
16553             return _this;
16554           };
16555
16556           var _assetPath = '';
16557           _this.assetPath = function(val) {
16558             if (!arguments.length) { return _assetPath; }
16559             _assetPath = val;
16560             return _this;
16561           };
16562
16563           var _assetMap = {};
16564           _this.assetMap = function(val) {
16565             if (!arguments.length) { return _assetMap; }
16566             _assetMap = val;
16567             return _this;
16568           };
16569
16570           _this.asset = function (val) {
16571             if (/^http(s)?:\/\//i.test(val)) { return val; }
16572             var filename = _assetPath + val;
16573             return _assetMap[filename] || filename;
16574           };
16575
16576           return _this;
16577         }
16578
16579         var _detected;
16580
16581         function utilDetect(refresh) {
16582           if (_detected && !refresh) { return _detected; }
16583           _detected = {};
16584
16585           var ua = navigator.userAgent;
16586           var m = null;
16587
16588           /* Browser */
16589           m = ua.match(/(edge)\/?\s*(\.?\d+(\.\d+)*)/i);   // Edge
16590           if (m !== null) {
16591             _detected.browser = m[1];
16592             _detected.version = m[2];
16593           }
16594           if (!_detected.browser) {
16595             m = ua.match(/Trident\/.*rv:([0-9]{1,}[\.0-9]{0,})/i);   // IE11
16596             if (m !== null) {
16597               _detected.browser = 'msie';
16598               _detected.version = m[1];
16599             }
16600           }
16601           if (!_detected.browser) {
16602             m = ua.match(/(opr)\/?\s*(\.?\d+(\.\d+)*)/i);   // Opera 15+
16603             if (m !== null) {
16604               _detected.browser = 'Opera';
16605               _detected.version = m[2];
16606             }
16607           }
16608           if (!_detected.browser) {
16609             m = ua.match(/(opera|chrome|safari|firefox|msie)\/?\s*(\.?\d+(\.\d+)*)/i);
16610             if (m !== null) {
16611               _detected.browser = m[1];
16612               _detected.version = m[2];
16613               m = ua.match(/version\/([\.\d]+)/i);
16614               if (m !== null) { _detected.version = m[1]; }
16615             }
16616           }
16617           if (!_detected.browser) {
16618             _detected.browser = navigator.appName;
16619             _detected.version = navigator.appVersion;
16620           }
16621
16622           // keep major.minor version only..
16623           _detected.version = _detected.version.split(/\W/).slice(0,2).join('.');
16624
16625           // detect other browser capabilities
16626           // Legacy Opera has incomplete svg style support. See #715
16627           _detected.opera = (_detected.browser.toLowerCase() === 'opera' && parseFloat(_detected.version) < 15 );
16628
16629           if (_detected.browser.toLowerCase() === 'msie') {
16630             _detected.ie = true;
16631             _detected.browser = 'Internet Explorer';
16632             _detected.support = parseFloat(_detected.version) >= 11;
16633           } else {
16634             _detected.ie = false;
16635             _detected.support = true;
16636           }
16637
16638           _detected.filedrop = (window.FileReader && 'ondrop' in window);
16639           _detected.download = !(_detected.ie || _detected.browser.toLowerCase() === 'edge');
16640           _detected.cssfilters = !(_detected.ie || _detected.browser.toLowerCase() === 'edge');
16641
16642
16643           /* Platform */
16644           if (/Win/.test(ua)) {
16645             _detected.os = 'win';
16646             _detected.platform = 'Windows';
16647           } else if (/Mac/.test(ua)) {
16648             _detected.os = 'mac';
16649             _detected.platform = 'Macintosh';
16650           } else if (/X11/.test(ua) || /Linux/.test(ua)) {
16651             _detected.os = 'linux';
16652             _detected.platform = 'Linux';
16653           } else {
16654             _detected.os = 'win';
16655             _detected.platform = 'Unknown';
16656           }
16657
16658           _detected.isMobileWebKit = (/\b(iPad|iPhone|iPod)\b/.test(ua) ||
16659             // HACK: iPadOS 13+ requests desktop sites by default by using a Mac user agent,
16660             // so assume any "mac" with multitouch is actually iOS
16661             (navigator.platform === 'MacIntel' && 'maxTouchPoints' in navigator && navigator.maxTouchPoints > 1)) &&
16662             /WebKit/.test(ua) &&
16663             !/Edge/.test(ua) &&
16664             !window.MSStream;
16665
16666
16667           /* Locale */
16668           // An array of locales requested by the browser in priority order.
16669           _detected.browserLocales = Array.from(new Set( // remove duplicates
16670               [navigator.language]
16671                 .concat(navigator.languages || [])
16672                 .concat([
16673                     // old property for backwards compatibility
16674                     navigator.userLanguage,
16675                     // fallback to English
16676                     'en'
16677                 ])
16678                 // remove any undefined values
16679                 .filter(Boolean)
16680             ));
16681
16682
16683           /* Host */
16684           var loc = window.top.location;
16685           var origin = loc.origin;
16686           if (!origin) {  // for unpatched IE11
16687             origin = loc.protocol + '//' + loc.hostname + (loc.port ? ':' + loc.port: '');
16688           }
16689
16690           _detected.host = origin + loc.pathname;
16691
16692
16693           return _detected;
16694         }
16695
16696         var aesJs = createCommonjsModule(function (module, exports) {
16697         /*! MIT License. Copyright 2015-2018 Richard Moore <me@ricmoo.com>. See LICENSE.txt. */
16698         (function(root) {
16699
16700             function checkInt(value) {
16701                 return (parseInt(value) === value);
16702             }
16703
16704             function checkInts(arrayish) {
16705                 if (!checkInt(arrayish.length)) { return false; }
16706
16707                 for (var i = 0; i < arrayish.length; i++) {
16708                     if (!checkInt(arrayish[i]) || arrayish[i] < 0 || arrayish[i] > 255) {
16709                         return false;
16710                     }
16711                 }
16712
16713                 return true;
16714             }
16715
16716             function coerceArray(arg, copy) {
16717
16718                 // ArrayBuffer view
16719                 if (arg.buffer && arg.name === 'Uint8Array') {
16720
16721                     if (copy) {
16722                         if (arg.slice) {
16723                             arg = arg.slice();
16724                         } else {
16725                             arg = Array.prototype.slice.call(arg);
16726                         }
16727                     }
16728
16729                     return arg;
16730                 }
16731
16732                 // It's an array; check it is a valid representation of a byte
16733                 if (Array.isArray(arg)) {
16734                     if (!checkInts(arg)) {
16735                         throw new Error('Array contains invalid value: ' + arg);
16736                     }
16737
16738                     return new Uint8Array(arg);
16739                 }
16740
16741                 // Something else, but behaves like an array (maybe a Buffer? Arguments?)
16742                 if (checkInt(arg.length) && checkInts(arg)) {
16743                     return new Uint8Array(arg);
16744                 }
16745
16746                 throw new Error('unsupported array-like object');
16747             }
16748
16749             function createArray(length) {
16750                 return new Uint8Array(length);
16751             }
16752
16753             function copyArray(sourceArray, targetArray, targetStart, sourceStart, sourceEnd) {
16754                 if (sourceStart != null || sourceEnd != null) {
16755                     if (sourceArray.slice) {
16756                         sourceArray = sourceArray.slice(sourceStart, sourceEnd);
16757                     } else {
16758                         sourceArray = Array.prototype.slice.call(sourceArray, sourceStart, sourceEnd);
16759                     }
16760                 }
16761                 targetArray.set(sourceArray, targetStart);
16762             }
16763
16764
16765
16766             var convertUtf8 = (function() {
16767                 function toBytes(text) {
16768                     var result = [], i = 0;
16769                     text = encodeURI(text);
16770                     while (i < text.length) {
16771                         var c = text.charCodeAt(i++);
16772
16773                         // if it is a % sign, encode the following 2 bytes as a hex value
16774                         if (c === 37) {
16775                             result.push(parseInt(text.substr(i, 2), 16));
16776                             i += 2;
16777
16778                         // otherwise, just the actual byte
16779                         } else {
16780                             result.push(c);
16781                         }
16782                     }
16783
16784                     return coerceArray(result);
16785                 }
16786
16787                 function fromBytes(bytes) {
16788                     var result = [], i = 0;
16789
16790                     while (i < bytes.length) {
16791                         var c = bytes[i];
16792
16793                         if (c < 128) {
16794                             result.push(String.fromCharCode(c));
16795                             i++;
16796                         } else if (c > 191 && c < 224) {
16797                             result.push(String.fromCharCode(((c & 0x1f) << 6) | (bytes[i + 1] & 0x3f)));
16798                             i += 2;
16799                         } else {
16800                             result.push(String.fromCharCode(((c & 0x0f) << 12) | ((bytes[i + 1] & 0x3f) << 6) | (bytes[i + 2] & 0x3f)));
16801                             i += 3;
16802                         }
16803                     }
16804
16805                     return result.join('');
16806                 }
16807
16808                 return {
16809                     toBytes: toBytes,
16810                     fromBytes: fromBytes,
16811                 }
16812             })();
16813
16814             var convertHex = (function() {
16815                 function toBytes(text) {
16816                     var result = [];
16817                     for (var i = 0; i < text.length; i += 2) {
16818                         result.push(parseInt(text.substr(i, 2), 16));
16819                     }
16820
16821                     return result;
16822                 }
16823
16824                 // http://ixti.net/development/javascript/2011/11/11/base64-encodedecode-of-utf8-in-browser-with-js.html
16825                 var Hex = '0123456789abcdef';
16826
16827                 function fromBytes(bytes) {
16828                         var result = [];
16829                         for (var i = 0; i < bytes.length; i++) {
16830                             var v = bytes[i];
16831                             result.push(Hex[(v & 0xf0) >> 4] + Hex[v & 0x0f]);
16832                         }
16833                         return result.join('');
16834                 }
16835
16836                 return {
16837                     toBytes: toBytes,
16838                     fromBytes: fromBytes,
16839                 }
16840             })();
16841
16842
16843             // Number of rounds by keysize
16844             var numberOfRounds = {16: 10, 24: 12, 32: 14};
16845
16846             // Round constant words
16847             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];
16848
16849             // S-box and Inverse S-box (S is for Substitution)
16850             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];
16851             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];
16852
16853             // Transformations for encryption
16854             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];
16855             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];
16856             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];
16857             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];
16858
16859             // Transformations for decryption
16860             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];
16861             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];
16862             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];
16863             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];
16864
16865             // Transformations for decryption key expansion
16866             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];
16867             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];
16868             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];
16869             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];
16870
16871             function convertToInt32(bytes) {
16872                 var result = [];
16873                 for (var i = 0; i < bytes.length; i += 4) {
16874                     result.push(
16875                         (bytes[i    ] << 24) |
16876                         (bytes[i + 1] << 16) |
16877                         (bytes[i + 2] <<  8) |
16878                          bytes[i + 3]
16879                     );
16880                 }
16881                 return result;
16882             }
16883
16884             var AES = function(key) {
16885                 if (!(this instanceof AES)) {
16886                     throw Error('AES must be instanitated with `new`');
16887                 }
16888
16889                 Object.defineProperty(this, 'key', {
16890                     value: coerceArray(key, true)
16891                 });
16892
16893                 this._prepare();
16894             };
16895
16896
16897             AES.prototype._prepare = function() {
16898
16899                 var rounds = numberOfRounds[this.key.length];
16900                 if (rounds == null) {
16901                     throw new Error('invalid key size (must be 16, 24 or 32 bytes)');
16902                 }
16903
16904                 // encryption round keys
16905                 this._Ke = [];
16906
16907                 // decryption round keys
16908                 this._Kd = [];
16909
16910                 for (var i = 0; i <= rounds; i++) {
16911                     this._Ke.push([0, 0, 0, 0]);
16912                     this._Kd.push([0, 0, 0, 0]);
16913                 }
16914
16915                 var roundKeyCount = (rounds + 1) * 4;
16916                 var KC = this.key.length / 4;
16917
16918                 // convert the key into ints
16919                 var tk = convertToInt32(this.key);
16920
16921                 // copy values into round key arrays
16922                 var index;
16923                 for (var i = 0; i < KC; i++) {
16924                     index = i >> 2;
16925                     this._Ke[index][i % 4] = tk[i];
16926                     this._Kd[rounds - index][i % 4] = tk[i];
16927                 }
16928
16929                 // key expansion (fips-197 section 5.2)
16930                 var rconpointer = 0;
16931                 var t = KC, tt;
16932                 while (t < roundKeyCount) {
16933                     tt = tk[KC - 1];
16934                     tk[0] ^= ((S[(tt >> 16) & 0xFF] << 24) ^
16935                               (S[(tt >>  8) & 0xFF] << 16) ^
16936                               (S[ tt        & 0xFF] <<  8) ^
16937                                S[(tt >> 24) & 0xFF]        ^
16938                               (rcon[rconpointer] << 24));
16939                     rconpointer += 1;
16940
16941                     // key expansion (for non-256 bit)
16942                     if (KC != 8) {
16943                         for (var i = 1; i < KC; i++) {
16944                             tk[i] ^= tk[i - 1];
16945                         }
16946
16947                     // key expansion for 256-bit keys is "slightly different" (fips-197)
16948                     } else {
16949                         for (var i = 1; i < (KC / 2); i++) {
16950                             tk[i] ^= tk[i - 1];
16951                         }
16952                         tt = tk[(KC / 2) - 1];
16953
16954                         tk[KC / 2] ^= (S[ tt        & 0xFF]        ^
16955                                       (S[(tt >>  8) & 0xFF] <<  8) ^
16956                                       (S[(tt >> 16) & 0xFF] << 16) ^
16957                                       (S[(tt >> 24) & 0xFF] << 24));
16958
16959                         for (var i = (KC / 2) + 1; i < KC; i++) {
16960                             tk[i] ^= tk[i - 1];
16961                         }
16962                     }
16963
16964                     // copy values into round key arrays
16965                     var i = 0, r, c;
16966                     while (i < KC && t < roundKeyCount) {
16967                         r = t >> 2;
16968                         c = t % 4;
16969                         this._Ke[r][c] = tk[i];
16970                         this._Kd[rounds - r][c] = tk[i++];
16971                         t++;
16972                     }
16973                 }
16974
16975                 // inverse-cipher-ify the decryption round key (fips-197 section 5.3)
16976                 for (var r = 1; r < rounds; r++) {
16977                     for (var c = 0; c < 4; c++) {
16978                         tt = this._Kd[r][c];
16979                         this._Kd[r][c] = (U1[(tt >> 24) & 0xFF] ^
16980                                           U2[(tt >> 16) & 0xFF] ^
16981                                           U3[(tt >>  8) & 0xFF] ^
16982                                           U4[ tt        & 0xFF]);
16983                     }
16984                 }
16985             };
16986
16987             AES.prototype.encrypt = function(plaintext) {
16988                 if (plaintext.length != 16) {
16989                     throw new Error('invalid plaintext size (must be 16 bytes)');
16990                 }
16991
16992                 var rounds = this._Ke.length - 1;
16993                 var a = [0, 0, 0, 0];
16994
16995                 // convert plaintext to (ints ^ key)
16996                 var t = convertToInt32(plaintext);
16997                 for (var i = 0; i < 4; i++) {
16998                     t[i] ^= this._Ke[0][i];
16999                 }
17000
17001                 // apply round transforms
17002                 for (var r = 1; r < rounds; r++) {
17003                     for (var i = 0; i < 4; i++) {
17004                         a[i] = (T1[(t[ i         ] >> 24) & 0xff] ^
17005                                 T2[(t[(i + 1) % 4] >> 16) & 0xff] ^
17006                                 T3[(t[(i + 2) % 4] >>  8) & 0xff] ^
17007                                 T4[ t[(i + 3) % 4]        & 0xff] ^
17008                                 this._Ke[r][i]);
17009                     }
17010                     t = a.slice();
17011                 }
17012
17013                 // the last round is special
17014                 var result = createArray(16), tt;
17015                 for (var i = 0; i < 4; i++) {
17016                     tt = this._Ke[rounds][i];
17017                     result[4 * i    ] = (S[(t[ i         ] >> 24) & 0xff] ^ (tt >> 24)) & 0xff;
17018                     result[4 * i + 1] = (S[(t[(i + 1) % 4] >> 16) & 0xff] ^ (tt >> 16)) & 0xff;
17019                     result[4 * i + 2] = (S[(t[(i + 2) % 4] >>  8) & 0xff] ^ (tt >>  8)) & 0xff;
17020                     result[4 * i + 3] = (S[ t[(i + 3) % 4]        & 0xff] ^  tt       ) & 0xff;
17021                 }
17022
17023                 return result;
17024             };
17025
17026             AES.prototype.decrypt = function(ciphertext) {
17027                 if (ciphertext.length != 16) {
17028                     throw new Error('invalid ciphertext size (must be 16 bytes)');
17029                 }
17030
17031                 var rounds = this._Kd.length - 1;
17032                 var a = [0, 0, 0, 0];
17033
17034                 // convert plaintext to (ints ^ key)
17035                 var t = convertToInt32(ciphertext);
17036                 for (var i = 0; i < 4; i++) {
17037                     t[i] ^= this._Kd[0][i];
17038                 }
17039
17040                 // apply round transforms
17041                 for (var r = 1; r < rounds; r++) {
17042                     for (var i = 0; i < 4; i++) {
17043                         a[i] = (T5[(t[ i          ] >> 24) & 0xff] ^
17044                                 T6[(t[(i + 3) % 4] >> 16) & 0xff] ^
17045                                 T7[(t[(i + 2) % 4] >>  8) & 0xff] ^
17046                                 T8[ t[(i + 1) % 4]        & 0xff] ^
17047                                 this._Kd[r][i]);
17048                     }
17049                     t = a.slice();
17050                 }
17051
17052                 // the last round is special
17053                 var result = createArray(16), tt;
17054                 for (var i = 0; i < 4; i++) {
17055                     tt = this._Kd[rounds][i];
17056                     result[4 * i    ] = (Si[(t[ i         ] >> 24) & 0xff] ^ (tt >> 24)) & 0xff;
17057                     result[4 * i + 1] = (Si[(t[(i + 3) % 4] >> 16) & 0xff] ^ (tt >> 16)) & 0xff;
17058                     result[4 * i + 2] = (Si[(t[(i + 2) % 4] >>  8) & 0xff] ^ (tt >>  8)) & 0xff;
17059                     result[4 * i + 3] = (Si[ t[(i + 1) % 4]        & 0xff] ^  tt       ) & 0xff;
17060                 }
17061
17062                 return result;
17063             };
17064
17065
17066             /**
17067              *  Mode Of Operation - Electonic Codebook (ECB)
17068              */
17069             var ModeOfOperationECB = function(key) {
17070                 if (!(this instanceof ModeOfOperationECB)) {
17071                     throw Error('AES must be instanitated with `new`');
17072                 }
17073
17074                 this.description = "Electronic Code Block";
17075                 this.name = "ecb";
17076
17077                 this._aes = new AES(key);
17078             };
17079
17080             ModeOfOperationECB.prototype.encrypt = function(plaintext) {
17081                 plaintext = coerceArray(plaintext);
17082
17083                 if ((plaintext.length % 16) !== 0) {
17084                     throw new Error('invalid plaintext size (must be multiple of 16 bytes)');
17085                 }
17086
17087                 var ciphertext = createArray(plaintext.length);
17088                 var block = createArray(16);
17089
17090                 for (var i = 0; i < plaintext.length; i += 16) {
17091                     copyArray(plaintext, block, 0, i, i + 16);
17092                     block = this._aes.encrypt(block);
17093                     copyArray(block, ciphertext, i);
17094                 }
17095
17096                 return ciphertext;
17097             };
17098
17099             ModeOfOperationECB.prototype.decrypt = function(ciphertext) {
17100                 ciphertext = coerceArray(ciphertext);
17101
17102                 if ((ciphertext.length % 16) !== 0) {
17103                     throw new Error('invalid ciphertext size (must be multiple of 16 bytes)');
17104                 }
17105
17106                 var plaintext = createArray(ciphertext.length);
17107                 var block = createArray(16);
17108
17109                 for (var i = 0; i < ciphertext.length; i += 16) {
17110                     copyArray(ciphertext, block, 0, i, i + 16);
17111                     block = this._aes.decrypt(block);
17112                     copyArray(block, plaintext, i);
17113                 }
17114
17115                 return plaintext;
17116             };
17117
17118
17119             /**
17120              *  Mode Of Operation - Cipher Block Chaining (CBC)
17121              */
17122             var ModeOfOperationCBC = function(key, iv) {
17123                 if (!(this instanceof ModeOfOperationCBC)) {
17124                     throw Error('AES must be instanitated with `new`');
17125                 }
17126
17127                 this.description = "Cipher Block Chaining";
17128                 this.name = "cbc";
17129
17130                 if (!iv) {
17131                     iv = createArray(16);
17132
17133                 } else if (iv.length != 16) {
17134                     throw new Error('invalid initialation vector size (must be 16 bytes)');
17135                 }
17136
17137                 this._lastCipherblock = coerceArray(iv, true);
17138
17139                 this._aes = new AES(key);
17140             };
17141
17142             ModeOfOperationCBC.prototype.encrypt = function(plaintext) {
17143                 plaintext = coerceArray(plaintext);
17144
17145                 if ((plaintext.length % 16) !== 0) {
17146                     throw new Error('invalid plaintext size (must be multiple of 16 bytes)');
17147                 }
17148
17149                 var ciphertext = createArray(plaintext.length);
17150                 var block = createArray(16);
17151
17152                 for (var i = 0; i < plaintext.length; i += 16) {
17153                     copyArray(plaintext, block, 0, i, i + 16);
17154
17155                     for (var j = 0; j < 16; j++) {
17156                         block[j] ^= this._lastCipherblock[j];
17157                     }
17158
17159                     this._lastCipherblock = this._aes.encrypt(block);
17160                     copyArray(this._lastCipherblock, ciphertext, i);
17161                 }
17162
17163                 return ciphertext;
17164             };
17165
17166             ModeOfOperationCBC.prototype.decrypt = function(ciphertext) {
17167                 ciphertext = coerceArray(ciphertext);
17168
17169                 if ((ciphertext.length % 16) !== 0) {
17170                     throw new Error('invalid ciphertext size (must be multiple of 16 bytes)');
17171                 }
17172
17173                 var plaintext = createArray(ciphertext.length);
17174                 var block = createArray(16);
17175
17176                 for (var i = 0; i < ciphertext.length; i += 16) {
17177                     copyArray(ciphertext, block, 0, i, i + 16);
17178                     block = this._aes.decrypt(block);
17179
17180                     for (var j = 0; j < 16; j++) {
17181                         plaintext[i + j] = block[j] ^ this._lastCipherblock[j];
17182                     }
17183
17184                     copyArray(ciphertext, this._lastCipherblock, 0, i, i + 16);
17185                 }
17186
17187                 return plaintext;
17188             };
17189
17190
17191             /**
17192              *  Mode Of Operation - Cipher Feedback (CFB)
17193              */
17194             var ModeOfOperationCFB = function(key, iv, segmentSize) {
17195                 if (!(this instanceof ModeOfOperationCFB)) {
17196                     throw Error('AES must be instanitated with `new`');
17197                 }
17198
17199                 this.description = "Cipher Feedback";
17200                 this.name = "cfb";
17201
17202                 if (!iv) {
17203                     iv = createArray(16);
17204
17205                 } else if (iv.length != 16) {
17206                     throw new Error('invalid initialation vector size (must be 16 size)');
17207                 }
17208
17209                 if (!segmentSize) { segmentSize = 1; }
17210
17211                 this.segmentSize = segmentSize;
17212
17213                 this._shiftRegister = coerceArray(iv, true);
17214
17215                 this._aes = new AES(key);
17216             };
17217
17218             ModeOfOperationCFB.prototype.encrypt = function(plaintext) {
17219                 if ((plaintext.length % this.segmentSize) != 0) {
17220                     throw new Error('invalid plaintext size (must be segmentSize bytes)');
17221                 }
17222
17223                 var encrypted = coerceArray(plaintext, true);
17224
17225                 var xorSegment;
17226                 for (var i = 0; i < encrypted.length; i += this.segmentSize) {
17227                     xorSegment = this._aes.encrypt(this._shiftRegister);
17228                     for (var j = 0; j < this.segmentSize; j++) {
17229                         encrypted[i + j] ^= xorSegment[j];
17230                     }
17231
17232                     // Shift the register
17233                     copyArray(this._shiftRegister, this._shiftRegister, 0, this.segmentSize);
17234                     copyArray(encrypted, this._shiftRegister, 16 - this.segmentSize, i, i + this.segmentSize);
17235                 }
17236
17237                 return encrypted;
17238             };
17239
17240             ModeOfOperationCFB.prototype.decrypt = function(ciphertext) {
17241                 if ((ciphertext.length % this.segmentSize) != 0) {
17242                     throw new Error('invalid ciphertext size (must be segmentSize bytes)');
17243                 }
17244
17245                 var plaintext = coerceArray(ciphertext, true);
17246
17247                 var xorSegment;
17248                 for (var i = 0; i < plaintext.length; i += this.segmentSize) {
17249                     xorSegment = this._aes.encrypt(this._shiftRegister);
17250
17251                     for (var j = 0; j < this.segmentSize; j++) {
17252                         plaintext[i + j] ^= xorSegment[j];
17253                     }
17254
17255                     // Shift the register
17256                     copyArray(this._shiftRegister, this._shiftRegister, 0, this.segmentSize);
17257                     copyArray(ciphertext, this._shiftRegister, 16 - this.segmentSize, i, i + this.segmentSize);
17258                 }
17259
17260                 return plaintext;
17261             };
17262
17263             /**
17264              *  Mode Of Operation - Output Feedback (OFB)
17265              */
17266             var ModeOfOperationOFB = function(key, iv) {
17267                 if (!(this instanceof ModeOfOperationOFB)) {
17268                     throw Error('AES must be instanitated with `new`');
17269                 }
17270
17271                 this.description = "Output Feedback";
17272                 this.name = "ofb";
17273
17274                 if (!iv) {
17275                     iv = createArray(16);
17276
17277                 } else if (iv.length != 16) {
17278                     throw new Error('invalid initialation vector size (must be 16 bytes)');
17279                 }
17280
17281                 this._lastPrecipher = coerceArray(iv, true);
17282                 this._lastPrecipherIndex = 16;
17283
17284                 this._aes = new AES(key);
17285             };
17286
17287             ModeOfOperationOFB.prototype.encrypt = function(plaintext) {
17288                 var encrypted = coerceArray(plaintext, true);
17289
17290                 for (var i = 0; i < encrypted.length; i++) {
17291                     if (this._lastPrecipherIndex === 16) {
17292                         this._lastPrecipher = this._aes.encrypt(this._lastPrecipher);
17293                         this._lastPrecipherIndex = 0;
17294                     }
17295                     encrypted[i] ^= this._lastPrecipher[this._lastPrecipherIndex++];
17296                 }
17297
17298                 return encrypted;
17299             };
17300
17301             // Decryption is symetric
17302             ModeOfOperationOFB.prototype.decrypt = ModeOfOperationOFB.prototype.encrypt;
17303
17304
17305             /**
17306              *  Counter object for CTR common mode of operation
17307              */
17308             var Counter = function(initialValue) {
17309                 if (!(this instanceof Counter)) {
17310                     throw Error('Counter must be instanitated with `new`');
17311                 }
17312
17313                 // We allow 0, but anything false-ish uses the default 1
17314                 if (initialValue !== 0 && !initialValue) { initialValue = 1; }
17315
17316                 if (typeof(initialValue) === 'number') {
17317                     this._counter = createArray(16);
17318                     this.setValue(initialValue);
17319
17320                 } else {
17321                     this.setBytes(initialValue);
17322                 }
17323             };
17324
17325             Counter.prototype.setValue = function(value) {
17326                 if (typeof(value) !== 'number' || parseInt(value) != value) {
17327                     throw new Error('invalid counter value (must be an integer)');
17328                 }
17329
17330                 // We cannot safely handle numbers beyond the safe range for integers
17331                 if (value > Number.MAX_SAFE_INTEGER) {
17332                     throw new Error('integer value out of safe range');
17333                 }
17334
17335                 for (var index = 15; index >= 0; --index) {
17336                     this._counter[index] = value % 256;
17337                     value = parseInt(value / 256);
17338                 }
17339             };
17340
17341             Counter.prototype.setBytes = function(bytes) {
17342                 bytes = coerceArray(bytes, true);
17343
17344                 if (bytes.length != 16) {
17345                     throw new Error('invalid counter bytes size (must be 16 bytes)');
17346                 }
17347
17348                 this._counter = bytes;
17349             };
17350
17351             Counter.prototype.increment = function() {
17352                 for (var i = 15; i >= 0; i--) {
17353                     if (this._counter[i] === 255) {
17354                         this._counter[i] = 0;
17355                     } else {
17356                         this._counter[i]++;
17357                         break;
17358                     }
17359                 }
17360             };
17361
17362
17363             /**
17364              *  Mode Of Operation - Counter (CTR)
17365              */
17366             var ModeOfOperationCTR = function(key, counter) {
17367                 if (!(this instanceof ModeOfOperationCTR)) {
17368                     throw Error('AES must be instanitated with `new`');
17369                 }
17370
17371                 this.description = "Counter";
17372                 this.name = "ctr";
17373
17374                 if (!(counter instanceof Counter)) {
17375                     counter = new Counter(counter);
17376                 }
17377
17378                 this._counter = counter;
17379
17380                 this._remainingCounter = null;
17381                 this._remainingCounterIndex = 16;
17382
17383                 this._aes = new AES(key);
17384             };
17385
17386             ModeOfOperationCTR.prototype.encrypt = function(plaintext) {
17387                 var encrypted = coerceArray(plaintext, true);
17388
17389                 for (var i = 0; i < encrypted.length; i++) {
17390                     if (this._remainingCounterIndex === 16) {
17391                         this._remainingCounter = this._aes.encrypt(this._counter._counter);
17392                         this._remainingCounterIndex = 0;
17393                         this._counter.increment();
17394                     }
17395                     encrypted[i] ^= this._remainingCounter[this._remainingCounterIndex++];
17396                 }
17397
17398                 return encrypted;
17399             };
17400
17401             // Decryption is symetric
17402             ModeOfOperationCTR.prototype.decrypt = ModeOfOperationCTR.prototype.encrypt;
17403
17404
17405             ///////////////////////
17406             // Padding
17407
17408             // See:https://tools.ietf.org/html/rfc2315
17409             function pkcs7pad(data) {
17410                 data = coerceArray(data, true);
17411                 var padder = 16 - (data.length % 16);
17412                 var result = createArray(data.length + padder);
17413                 copyArray(data, result);
17414                 for (var i = data.length; i < result.length; i++) {
17415                     result[i] = padder;
17416                 }
17417                 return result;
17418             }
17419
17420             function pkcs7strip(data) {
17421                 data = coerceArray(data, true);
17422                 if (data.length < 16) { throw new Error('PKCS#7 invalid length'); }
17423
17424                 var padder = data[data.length - 1];
17425                 if (padder > 16) { throw new Error('PKCS#7 padding byte out of range'); }
17426
17427                 var length = data.length - padder;
17428                 for (var i = 0; i < padder; i++) {
17429                     if (data[length + i] !== padder) {
17430                         throw new Error('PKCS#7 invalid padding byte');
17431                     }
17432                 }
17433
17434                 var result = createArray(length);
17435                 copyArray(data, result, 0, 0, length);
17436                 return result;
17437             }
17438
17439             ///////////////////////
17440             // Exporting
17441
17442
17443             // The block cipher
17444             var aesjs = {
17445                 AES: AES,
17446                 Counter: Counter,
17447
17448                 ModeOfOperation: {
17449                     ecb: ModeOfOperationECB,
17450                     cbc: ModeOfOperationCBC,
17451                     cfb: ModeOfOperationCFB,
17452                     ofb: ModeOfOperationOFB,
17453                     ctr: ModeOfOperationCTR
17454                 },
17455
17456                 utils: {
17457                     hex: convertHex,
17458                     utf8: convertUtf8
17459                 },
17460
17461                 padding: {
17462                     pkcs7: {
17463                         pad: pkcs7pad,
17464                         strip: pkcs7strip
17465                     }
17466                 },
17467
17468                 _arrayTest: {
17469                     coerceArray: coerceArray,
17470                     createArray: createArray,
17471                     copyArray: copyArray,
17472                 }
17473             };
17474
17475
17476             // node.js
17477             {
17478                 module.exports = aesjs;
17479
17480             // RequireJS/AMD
17481             // http://www.requirejs.org/docs/api.html
17482             // https://github.com/amdjs/amdjs-api/wiki/AMD
17483             }
17484
17485
17486         })();
17487         });
17488
17489         // See https://github.com/ricmoo/aes-js
17490         // We can use keys that are 128 bits (16 bytes), 192 bits (24 bytes) or 256 bits (32 bytes).
17491         // To generate a random key:  window.crypto.getRandomValues(new Uint8Array(16));
17492
17493         // This default signing key is built into iD and can be used to mask/unmask sensitive values.
17494         var DEFAULT_128 = [250, 157, 60, 79, 142, 134, 229, 129, 138, 126, 210, 129, 29, 71, 160, 208];
17495
17496
17497         function utilAesEncrypt(text, key) {
17498           key = key || DEFAULT_128;
17499           var textBytes = aesJs.utils.utf8.toBytes(text);
17500           var aesCtr = new aesJs.ModeOfOperation.ctr(key);
17501           var encryptedBytes = aesCtr.encrypt(textBytes);
17502           var encryptedHex = aesJs.utils.hex.fromBytes(encryptedBytes);
17503           return encryptedHex;
17504         }
17505
17506
17507         function utilAesDecrypt(encryptedHex, key) {
17508           key = key || DEFAULT_128;
17509           var encryptedBytes = aesJs.utils.hex.toBytes(encryptedHex);
17510           var aesCtr = new aesJs.ModeOfOperation.ctr(key);
17511           var decryptedBytes = aesCtr.decrypt(encryptedBytes);
17512           var text = aesJs.utils.utf8.fromBytes(decryptedBytes);
17513           return text;
17514         }
17515
17516         function utilCleanTags(tags) {
17517             var out = {};
17518             for (var k in tags) {
17519                 if (!k) { continue; }
17520                 var v = tags[k];
17521                 if (v !== undefined) {
17522                     out[k] = cleanValue(k, v);
17523                 }
17524             }
17525
17526             return out;
17527
17528
17529             function cleanValue(k, v) {
17530                 function keepSpaces(k) {
17531                     return /_hours|_times|:conditional$/.test(k);
17532                 }
17533
17534                 function skip(k) {
17535                     return /^(description|note|fixme)$/.test(k);
17536                 }
17537
17538                 if (skip(k)) { return v; }
17539
17540                 var cleaned = v
17541                     .split(';')
17542                     .map(function(s) { return s.trim(); })
17543                     .join(keepSpaces(k) ? '; ' : ';');
17544
17545                 // The code below is not intended to validate websites and emails.
17546                 // It is only intended to prevent obvious copy-paste errors. (#2323)
17547                 // clean website- and email-like tags
17548                 if (k.indexOf('website') !== -1 ||
17549                     k.indexOf('email') !== -1 ||
17550                     cleaned.indexOf('http') === 0) {
17551                     cleaned = cleaned
17552                         .replace(/[\u200B-\u200F\uFEFF]/g, '');  // strip LRM and other zero width chars
17553
17554                 }
17555
17556                 return cleaned;
17557             }
17558         }
17559
17560         // Like selection.property('value', ...), but avoids no-op value sets,
17561         // which can result in layout/repaint thrashing in some situations.
17562         function utilGetSetValue(selection, value) {
17563             function d3_selection_value(value) {
17564                 function valueNull() {
17565                     delete this.value;
17566                 }
17567
17568                 function valueConstant() {
17569                     if (this.value !== value) {
17570                         this.value = value;
17571                     }
17572                 }
17573
17574                 function valueFunction() {
17575                     var x = value.apply(this, arguments);
17576                     if (x == null) {
17577                         delete this.value;
17578                     } else if (this.value !== x) {
17579                         this.value = x;
17580                     }
17581                 }
17582
17583                 return value == null
17584                     ? valueNull : (typeof value === 'function'
17585                     ? valueFunction : valueConstant);
17586             }
17587
17588             if (arguments.length === 1) {
17589                 return selection.property('value');
17590             }
17591
17592             return selection.each(d3_selection_value(value));
17593         }
17594
17595         function utilKeybinding(namespace) {
17596             var _keybindings = {};
17597
17598
17599             function testBindings(isCapturing) {
17600                 var didMatch = false;
17601                 var bindings = Object.keys(_keybindings).map(function(id) { return _keybindings[id]; });
17602                 var i, binding;
17603
17604                 // Most key shortcuts will accept either lower or uppercase ('h' or 'H'),
17605                 // so we don't strictly match on the shift key, but we prioritize
17606                 // shifted keybindings first, and fallback to unshifted only if no match.
17607                 // (This lets us differentiate between '←'/'⇧←' or '⌘Z'/'⌘⇧Z')
17608
17609                 // priority match shifted keybindings first
17610                 for (i = 0; i < bindings.length; i++) {
17611                     binding = bindings[i];
17612                     if (!binding.event.modifiers.shiftKey) { continue; }  // no shift
17613                     if (!!binding.capture !== isCapturing) { continue; }
17614                     if (matches(binding, true)) {
17615                         binding.callback();
17616                         didMatch = true;
17617                     }
17618                 }
17619
17620                 // then unshifted keybindings
17621                 if (didMatch) { return; }
17622                 for (i = 0; i < bindings.length; i++) {
17623                     binding = bindings[i];
17624                     if (binding.event.modifiers.shiftKey) { continue; }   // shift
17625                     if (!!binding.capture !== isCapturing) { continue; }
17626                     if (matches(binding, false)) {
17627                         binding.callback();
17628                     }
17629                 }
17630
17631
17632                 function matches(binding, testShift) {
17633                     var event$1 = event;
17634                     var isMatch = false;
17635                     var tryKeyCode = true;
17636
17637                     // Prefer a match on `KeyboardEvent.key`
17638                     if (event$1.key !== undefined) {
17639                         tryKeyCode = (event$1.key.charCodeAt(0) > 255);  // outside ISO-Latin-1
17640                         isMatch = true;
17641
17642                         if (binding.event.key === undefined) {
17643                             isMatch = false;
17644                         } else if (Array.isArray(binding.event.key)) {
17645                             if (binding.event.key.map(function(s) { return s.toLowerCase(); }).indexOf(event$1.key.toLowerCase()) === -1)
17646                                 { isMatch = false; }
17647                         } else {
17648                             if (event$1.key.toLowerCase() !== binding.event.key.toLowerCase())
17649                                 { isMatch = false; }
17650                         }
17651                     }
17652
17653                     // Fallback match on `KeyboardEvent.keyCode`, can happen if:
17654                     // - browser doesn't support `KeyboardEvent.key`
17655                     // - `KeyboardEvent.key` is outside ISO-Latin-1 range (cyrillic?)
17656                     if (!isMatch && tryKeyCode) {
17657                         isMatch = (event$1.keyCode === binding.event.keyCode);
17658                     }
17659
17660                     if (!isMatch) { return false; }
17661
17662                     // test modifier keys
17663                     if (!(event$1.ctrlKey && event$1.altKey)) {  // if both are set, assume AltGr and skip it - #4096
17664                         if (event$1.ctrlKey !== binding.event.modifiers.ctrlKey) { return false; }
17665                         if (event$1.altKey !== binding.event.modifiers.altKey) { return false; }
17666                     }
17667                     if (event$1.metaKey !== binding.event.modifiers.metaKey) { return false; }
17668                     if (testShift && event$1.shiftKey !== binding.event.modifiers.shiftKey) { return false; }
17669
17670                     return true;
17671                 }
17672             }
17673
17674
17675             function capture() {
17676                 testBindings(true);
17677             }
17678
17679
17680             function bubble() {
17681                 var tagName = select(event.target).node().tagName;
17682                 if (tagName === 'INPUT' || tagName === 'SELECT' || tagName === 'TEXTAREA') {
17683                     return;
17684                 }
17685                 testBindings(false);
17686             }
17687
17688
17689             function keybinding(selection) {
17690                 selection = selection || select(document);
17691                 selection.on('keydown.capture.' + namespace, capture, true);
17692                 selection.on('keydown.bubble.' + namespace, bubble, false);
17693                 return keybinding;
17694             }
17695
17696             // was: keybinding.off()
17697             keybinding.unbind = function(selection) {
17698                 _keybindings = [];
17699                 selection = selection || select(document);
17700                 selection.on('keydown.capture.' + namespace, null);
17701                 selection.on('keydown.bubble.' + namespace, null);
17702                 return keybinding;
17703             };
17704
17705
17706             keybinding.clear = function() {
17707                 _keybindings = {};
17708                 return keybinding;
17709             };
17710
17711
17712             // Remove one or more keycode bindings.
17713             keybinding.off = function(codes, capture) {
17714                 var arr = utilArrayUniq([].concat(codes));
17715
17716                 for (var i = 0; i < arr.length; i++) {
17717                     var id = arr[i] + (capture ? '-capture' : '-bubble');
17718                     delete _keybindings[id];
17719                 }
17720                 return keybinding;
17721             };
17722
17723
17724             // Add one or more keycode bindings.
17725             keybinding.on = function(codes, callback, capture) {
17726                 if (typeof callback !== 'function') {
17727                     return keybinding.off(codes, capture);
17728                 }
17729
17730                 var arr = utilArrayUniq([].concat(codes));
17731
17732                 for (var i = 0; i < arr.length; i++) {
17733                     var id = arr[i] + (capture ? '-capture' : '-bubble');
17734                     var binding = {
17735                         id: id,
17736                         capture: capture,
17737                         callback: callback,
17738                         event: {
17739                             key: undefined,  // preferred
17740                             keyCode: 0,      // fallback
17741                             modifiers: {
17742                                 shiftKey: false,
17743                                 ctrlKey: false,
17744                                 altKey: false,
17745                                 metaKey: false
17746                             }
17747                         }
17748                     };
17749
17750                     if (_keybindings[id]) {
17751                         console.warn('warning: duplicate keybinding for "' + id + '"'); // eslint-disable-line no-console
17752                     }
17753
17754                     _keybindings[id] = binding;
17755
17756                     var matches = arr[i].toLowerCase().match(/(?:(?:[^+⇧⌃⌥⌘])+|[⇧⌃⌥⌘]|\+\+|^\+$)/g);
17757                     for (var j = 0; j < matches.length; j++) {
17758                         // Normalise matching errors
17759                         if (matches[j] === '++') { matches[j] = '+'; }
17760
17761                         if (matches[j] in utilKeybinding.modifierCodes) {
17762                             var prop = utilKeybinding.modifierProperties[utilKeybinding.modifierCodes[matches[j]]];
17763                             binding.event.modifiers[prop] = true;
17764                         } else {
17765                             binding.event.key = utilKeybinding.keys[matches[j]] || matches[j];
17766                             if (matches[j] in utilKeybinding.keyCodes) {
17767                                 binding.event.keyCode = utilKeybinding.keyCodes[matches[j]];
17768                             }
17769                         }
17770                     }
17771                 }
17772
17773                 return keybinding;
17774             };
17775
17776
17777             return keybinding;
17778         }
17779
17780
17781         /*
17782          * See https://github.com/keithamus/jwerty
17783          */
17784
17785         utilKeybinding.modifierCodes = {
17786             // Shift key, ⇧
17787             '⇧': 16, shift: 16,
17788             // CTRL key, on Mac: ⌃
17789             '⌃': 17, ctrl: 17,
17790             // ALT key, on Mac: ⌥ (Alt)
17791             '⌥': 18, alt: 18, option: 18,
17792             // META, on Mac: ⌘ (CMD), on Windows (Win), on Linux (Super)
17793             '⌘': 91, meta: 91, cmd: 91, 'super': 91, win: 91
17794         };
17795
17796         utilKeybinding.modifierProperties = {
17797             16: 'shiftKey',
17798             17: 'ctrlKey',
17799             18: 'altKey',
17800             91: 'metaKey'
17801         };
17802
17803         utilKeybinding.keys = {
17804             // Backspace key, on Mac: ⌫ (Backspace)
17805             '⌫': 'Backspace', backspace: 'Backspace',
17806             // Tab Key, on Mac: ⇥ (Tab), on Windows ⇥⇥
17807             '⇥': 'Tab', '⇆': 'Tab', tab: 'Tab',
17808             // Return key, ↩
17809             '↩': 'Enter', 'return': 'Enter', enter: 'Enter', '⌅': 'Enter',
17810             // Pause/Break key
17811             'pause': 'Pause', 'pause-break': 'Pause',
17812             // Caps Lock key, ⇪
17813             '⇪': 'CapsLock', caps: 'CapsLock', 'caps-lock': 'CapsLock',
17814             // Escape key, on Mac: ⎋, on Windows: Esc
17815             '⎋': ['Escape', 'Esc'], escape: ['Escape', 'Esc'], esc: ['Escape', 'Esc'],
17816             // Space key
17817             space: [' ', 'Spacebar'],
17818             // Page-Up key, or pgup, on Mac: ↖
17819             '↖': 'PageUp', pgup: 'PageUp', 'page-up': 'PageUp',
17820             // Page-Down key, or pgdown, on Mac: ↘
17821             '↘': 'PageDown', pgdown: 'PageDown', 'page-down': 'PageDown',
17822             // END key, on Mac: ⇟
17823             '⇟': 'End', end: 'End',
17824             // HOME key, on Mac: ⇞
17825             '⇞': 'Home', home: 'Home',
17826             // Insert key, or ins
17827             ins: 'Insert', insert: 'Insert',
17828             // Delete key, on Mac: ⌦ (Delete)
17829             '⌦': ['Delete', 'Del'], del: ['Delete', 'Del'], 'delete': ['Delete', 'Del'],
17830             // Left Arrow Key, or ←
17831             '←': ['ArrowLeft', 'Left'], left: ['ArrowLeft', 'Left'], 'arrow-left': ['ArrowLeft', 'Left'],
17832             // Up Arrow Key, or ↑
17833             '↑': ['ArrowUp', 'Up'], up: ['ArrowUp', 'Up'], 'arrow-up': ['ArrowUp', 'Up'],
17834             // Right Arrow Key, or →
17835             '→': ['ArrowRight', 'Right'], right: ['ArrowRight', 'Right'], 'arrow-right': ['ArrowRight', 'Right'],
17836             // Up Arrow Key, or ↓
17837             '↓': ['ArrowDown', 'Down'], down: ['ArrowDown', 'Down'], 'arrow-down': ['ArrowDown', 'Down'],
17838             // odities, stuff for backward compatibility (browsers and code):
17839             // Num-Multiply, or *
17840             '*': ['*', 'Multiply'], star: ['*', 'Multiply'], asterisk: ['*', 'Multiply'], multiply: ['*', 'Multiply'],
17841             // Num-Plus or +
17842             '+': ['+', 'Add'], 'plus': ['+', 'Add'],
17843             // Num-Subtract, or -
17844             '-': ['-', 'Subtract'], subtract: ['-', 'Subtract'], 'dash': ['-', 'Subtract'],
17845             // Semicolon
17846             semicolon: ';',
17847             // = or equals
17848             equals: '=',
17849             // Comma, or ,
17850             comma: ',',
17851             // Period, or ., or full-stop
17852             period: '.', 'full-stop': '.',
17853             // Slash, or /, or forward-slash
17854             slash: '/', 'forward-slash': '/',
17855             // Tick, or `, or back-quote
17856             tick: '`', 'back-quote': '`',
17857             // Open bracket, or [
17858             'open-bracket': '[',
17859             // Back slash, or \
17860             'back-slash': '\\',
17861             // Close backet, or ]
17862             'close-bracket': ']',
17863             // Apostrophe, or Quote, or '
17864             quote: '\'', apostrophe: '\'',
17865             // NUMPAD 0-9
17866             'num-0': '0',
17867             'num-1': '1',
17868             'num-2': '2',
17869             'num-3': '3',
17870             'num-4': '4',
17871             'num-5': '5',
17872             'num-6': '6',
17873             'num-7': '7',
17874             'num-8': '8',
17875             'num-9': '9',
17876             // F1-F25
17877             f1: 'F1',
17878             f2: 'F2',
17879             f3: 'F3',
17880             f4: 'F4',
17881             f5: 'F5',
17882             f6: 'F6',
17883             f7: 'F7',
17884             f8: 'F8',
17885             f9: 'F9',
17886             f10: 'F10',
17887             f11: 'F11',
17888             f12: 'F12',
17889             f13: 'F13',
17890             f14: 'F14',
17891             f15: 'F15',
17892             f16: 'F16',
17893             f17: 'F17',
17894             f18: 'F18',
17895             f19: 'F19',
17896             f20: 'F20',
17897             f21: 'F21',
17898             f22: 'F22',
17899             f23: 'F23',
17900             f24: 'F24',
17901             f25: 'F25'
17902         };
17903
17904         utilKeybinding.keyCodes = {
17905             // Backspace key, on Mac: ⌫ (Backspace)
17906             '⌫': 8, backspace: 8,
17907             // Tab Key, on Mac: ⇥ (Tab), on Windows ⇥⇥
17908             '⇥': 9, '⇆': 9, tab: 9,
17909             // Return key, ↩
17910             '↩': 13, 'return': 13, enter: 13, '⌅': 13,
17911             // Pause/Break key
17912             'pause': 19, 'pause-break': 19,
17913             // Caps Lock key, ⇪
17914             '⇪': 20, caps: 20, 'caps-lock': 20,
17915             // Escape key, on Mac: ⎋, on Windows: Esc
17916             '⎋': 27, escape: 27, esc: 27,
17917             // Space key
17918             space: 32,
17919             // Page-Up key, or pgup, on Mac: ↖
17920             '↖': 33, pgup: 33, 'page-up': 33,
17921             // Page-Down key, or pgdown, on Mac: ↘
17922             '↘': 34, pgdown: 34, 'page-down': 34,
17923             // END key, on Mac: ⇟
17924             '⇟': 35, end: 35,
17925             // HOME key, on Mac: ⇞
17926             '⇞': 36, home: 36,
17927             // Insert key, or ins
17928             ins: 45, insert: 45,
17929             // Delete key, on Mac: ⌦ (Delete)
17930             '⌦': 46, del: 46, 'delete': 46,
17931             // Left Arrow Key, or ←
17932             '←': 37, left: 37, 'arrow-left': 37,
17933             // Up Arrow Key, or ↑
17934             '↑': 38, up: 38, 'arrow-up': 38,
17935             // Right Arrow Key, or →
17936             '→': 39, right: 39, 'arrow-right': 39,
17937             // Up Arrow Key, or ↓
17938             '↓': 40, down: 40, 'arrow-down': 40,
17939             // odities, printing characters that come out wrong:
17940             // Firefox Equals
17941             'ffequals': 61,
17942             // Num-Multiply, or *
17943             '*': 106, star: 106, asterisk: 106, multiply: 106,
17944             // Num-Plus or +
17945             '+': 107, 'plus': 107,
17946             // Num-Subtract, or -
17947             '-': 109, subtract: 109,
17948             // Firefox Plus
17949             'ffplus': 171,
17950             // Firefox Minus
17951             'ffminus': 173,
17952             // Semicolon
17953             ';': 186, semicolon: 186,
17954             // = or equals
17955             '=': 187, 'equals': 187,
17956             // Comma, or ,
17957             ',': 188, comma: 188,
17958             // Dash / Underscore key
17959             'dash': 189,
17960             // Period, or ., or full-stop
17961             '.': 190, period: 190, 'full-stop': 190,
17962             // Slash, or /, or forward-slash
17963             '/': 191, slash: 191, 'forward-slash': 191,
17964             // Tick, or `, or back-quote
17965             '`': 192, tick: 192, 'back-quote': 192,
17966             // Open bracket, or [
17967             '[': 219, 'open-bracket': 219,
17968             // Back slash, or \
17969             '\\': 220, 'back-slash': 220,
17970             // Close backet, or ]
17971             ']': 221, 'close-bracket': 221,
17972             // Apostrophe, or Quote, or '
17973             '\'': 222, quote: 222, apostrophe: 222
17974         };
17975
17976         // NUMPAD 0-9
17977         var i$2 = 95, n = 0;
17978         while (++i$2 < 106) {
17979             utilKeybinding.keyCodes['num-' + n] = i$2;
17980             ++n;
17981         }
17982
17983         // 0-9
17984         i$2 = 47; n = 0;
17985         while (++i$2 < 58) {
17986             utilKeybinding.keyCodes[n] = i$2;
17987             ++n;
17988         }
17989
17990         // F1-F25
17991         i$2 = 111; n = 1;
17992         while (++i$2 < 136) {
17993             utilKeybinding.keyCodes['f' + n] = i$2;
17994             ++n;
17995         }
17996
17997         // a-z
17998         i$2 = 64;
17999         while (++i$2 < 91) {
18000             utilKeybinding.keyCodes[String.fromCharCode(i$2).toLowerCase()] = i$2;
18001         }
18002
18003         function utilObjectOmit(obj, omitKeys) {
18004             return Object.keys(obj).reduce(function(result, key) {
18005                 if (omitKeys.indexOf(key) === -1) {
18006                     result[key] = obj[key];  // keep
18007                 }
18008                 return result;
18009             }, {});
18010         }
18011
18012         // Copies a variable number of methods from source to target.
18013         function utilRebind(target, source) {
18014             var arguments$1 = arguments;
18015
18016             var i = 1, n = arguments.length, method;
18017             while (++i < n) {
18018                 target[method = arguments$1[i]] = d3_rebind(target, source, source[method]);
18019             }
18020             return target;
18021         }
18022
18023         // Method is assumed to be a standard D3 getter-setter:
18024         // If passed with no arguments, gets the value.
18025         // If passed with arguments, sets the value and returns the target.
18026         function d3_rebind(target, source, method) {
18027             return function() {
18028                 var value = method.apply(source, arguments);
18029                 return value === source ? target : value;
18030             };
18031         }
18032
18033         // A per-domain session mutex backed by a cookie and dead man's
18034         // switch. If the session crashes, the mutex will auto-release
18035         // after 5 seconds.
18036
18037         // This accepts a string and returns an object that complies with utilSessionMutexType
18038         function utilSessionMutex(name) {
18039             var mutex = {};
18040             var intervalID;
18041
18042             function renew() {
18043                 var expires = new Date();
18044                 expires.setSeconds(expires.getSeconds() + 5);
18045                 document.cookie = name + '=1; expires=' + expires.toUTCString() + '; sameSite=strict';
18046             }
18047
18048             mutex.lock = function () {
18049                 if (intervalID) { return true; }
18050                 var cookie = document.cookie.replace(new RegExp('(?:(?:^|.*;)\\s*' + name + '\\s*\\=\\s*([^;]*).*$)|^.*$'), '$1');
18051                 if (cookie) { return false; }
18052                 renew();
18053                 intervalID = window.setInterval(renew, 4000);
18054                 return true;
18055             };
18056
18057             mutex.unlock = function () {
18058                 if (!intervalID) { return; }
18059                 document.cookie = name + '=; expires=Thu, 01 Jan 1970 00:00:00 GMT; sameSite=strict';
18060                 clearInterval(intervalID);
18061                 intervalID = null;
18062             };
18063
18064             mutex.locked = function () {
18065                 return !!intervalID;
18066             };
18067
18068             return mutex;
18069         }
18070
18071         function utilTiler() {
18072             var _size = [256, 256];
18073             var _scale = 256;
18074             var _tileSize = 256;
18075             var _zoomExtent = [0, 20];
18076             var _translate = [_size[0] / 2, _size[1] / 2];
18077             var _margin = 0;
18078             var _skipNullIsland = false;
18079
18080
18081             function clamp(num, min, max) {
18082                 return Math.max(min, Math.min(num, max));
18083             }
18084
18085
18086             function nearNullIsland(tile) {
18087                 var x = tile[0];
18088                 var y = tile[1];
18089                 var z = tile[2];
18090                 if (z >= 7) {
18091                     var center = Math.pow(2, z - 1);
18092                     var width = Math.pow(2, z - 6);
18093                     var min = center - (width / 2);
18094                     var max = center + (width / 2) - 1;
18095                     return x >= min && x <= max && y >= min && y <= max;
18096                 }
18097                 return false;
18098             }
18099
18100
18101             function tiler() {
18102                 var z = geoScaleToZoom(_scale / (2 * Math.PI), _tileSize);
18103                 var z0 = clamp(Math.round(z), _zoomExtent[0], _zoomExtent[1]);
18104                 var tileMin = 0;
18105                 var tileMax = Math.pow(2, z0) - 1;
18106                 var log2ts = Math.log(_tileSize) * Math.LOG2E;
18107                 var k = Math.pow(2, z - z0 + log2ts);
18108                 var origin = [
18109                     (_translate[0] - _scale / 2) / k,
18110                     (_translate[1] - _scale / 2) / k
18111                 ];
18112
18113                 var cols = range$1(
18114                     clamp(Math.floor(-origin[0]) - _margin,               tileMin, tileMax + 1),
18115                     clamp(Math.ceil(_size[0] / k - origin[0]) + _margin,  tileMin, tileMax + 1)
18116                 );
18117                 var rows = range$1(
18118                     clamp(Math.floor(-origin[1]) - _margin,               tileMin, tileMax + 1),
18119                     clamp(Math.ceil(_size[1] / k - origin[1]) + _margin,  tileMin, tileMax + 1)
18120                 );
18121
18122                 var tiles = [];
18123                 for (var i = 0; i < rows.length; i++) {
18124                     var y = rows[i];
18125                     for (var j = 0; j < cols.length; j++) {
18126                         var x = cols[j];
18127
18128                         if (i >= _margin && i <= rows.length - _margin &&
18129                             j >= _margin && j <= cols.length - _margin) {
18130                             tiles.unshift([x, y, z0]);  // tiles in view at beginning
18131                         } else {
18132                             tiles.push([x, y, z0]);     // tiles in margin at the end
18133                         }
18134                     }
18135                 }
18136
18137                 tiles.translate = origin;
18138                 tiles.scale = k;
18139
18140                 return tiles;
18141             }
18142
18143
18144             /**
18145              * getTiles() returns an array of tiles that cover the map view
18146              */
18147             tiler.getTiles = function(projection) {
18148                 var origin = [
18149                     projection.scale() * Math.PI - projection.translate()[0],
18150                     projection.scale() * Math.PI - projection.translate()[1]
18151                 ];
18152
18153                 this
18154                     .size(projection.clipExtent()[1])
18155                     .scale(projection.scale() * 2 * Math.PI)
18156                     .translate(projection.translate());
18157
18158                 var tiles = tiler();
18159                 var ts = tiles.scale;
18160
18161                 return tiles
18162                     .map(function(tile) {
18163                         if (_skipNullIsland && nearNullIsland(tile)) {
18164                             return false;
18165                         }
18166                         var x = tile[0] * ts - origin[0];
18167                         var y = tile[1] * ts - origin[1];
18168                         return {
18169                             id: tile.toString(),
18170                             xyz: tile,
18171                             extent: geoExtent(
18172                                 projection.invert([x, y + ts]),
18173                                 projection.invert([x + ts, y])
18174                             )
18175                         };
18176                     }).filter(Boolean);
18177             };
18178
18179
18180             /**
18181              * getGeoJSON() returns a FeatureCollection for debugging tiles
18182              */
18183             tiler.getGeoJSON = function(projection) {
18184                 var features = tiler.getTiles(projection).map(function(tile) {
18185                     return {
18186                         type: 'Feature',
18187                         properties: {
18188                             id: tile.id,
18189                             name: tile.id
18190                         },
18191                         geometry: {
18192                             type: 'Polygon',
18193                             coordinates: [ tile.extent.polygon() ]
18194                         }
18195                     };
18196                 });
18197
18198                 return {
18199                     type: 'FeatureCollection',
18200                     features: features
18201                 };
18202             };
18203
18204
18205             tiler.tileSize = function(val) {
18206                 if (!arguments.length) { return _tileSize; }
18207                 _tileSize = val;
18208                 return tiler;
18209             };
18210
18211
18212             tiler.zoomExtent = function(val) {
18213                 if (!arguments.length) { return _zoomExtent; }
18214                 _zoomExtent = val;
18215                 return tiler;
18216             };
18217
18218
18219             tiler.size = function(val) {
18220                 if (!arguments.length) { return _size; }
18221                 _size = val;
18222                 return tiler;
18223             };
18224
18225
18226             tiler.scale = function(val) {
18227                 if (!arguments.length) { return _scale; }
18228                 _scale = val;
18229                 return tiler;
18230             };
18231
18232
18233             tiler.translate = function(val) {
18234                 if (!arguments.length) { return _translate; }
18235                 _translate = val;
18236                 return tiler;
18237             };
18238
18239
18240             // number to extend the rows/columns beyond those covering the viewport
18241             tiler.margin = function(val) {
18242                 if (!arguments.length) { return _margin; }
18243                 _margin = +val;
18244                 return tiler;
18245             };
18246
18247
18248             tiler.skipNullIsland = function(val) {
18249                 if (!arguments.length) { return _skipNullIsland; }
18250                 _skipNullIsland = val;
18251                 return tiler;
18252             };
18253
18254
18255             return tiler;
18256         }
18257
18258         function utilTriggerEvent(target, type) {
18259             target.each(function() {
18260                 var evt = document.createEvent('HTMLEvents');
18261                 evt.initEvent(type, true, true);
18262                 this.dispatchEvent(evt);
18263             });
18264         }
18265
18266         var _mainLocalizer = coreLocalizer(); // singleton
18267         var _t = _mainLocalizer.t;
18268
18269         //
18270         // coreLocalizer manages language and locale parameters including translated strings
18271         //
18272         function coreLocalizer() {
18273
18274             var localizer = {};
18275
18276             var _dataLanguages = {};
18277
18278             // `localeData` is an object containing all _supported_ locale codes -> language info.
18279             // {
18280             // en: { rtl: false, languageNames: {…}, scriptNames: {…} },
18281             // de: { rtl: false, languageNames: {…}, scriptNames: {…} },
18282             // …
18283             // }
18284             var _dataLocales = {};
18285
18286             // `localeStrings` is an object containing all _loaded_ locale codes -> string data.
18287             // {
18288             // en: { icons: {…}, toolbar: {…}, modes: {…}, operations: {…}, … },
18289             // de: { icons: {…}, toolbar: {…}, modes: {…}, operations: {…}, … },
18290             // …
18291             // }
18292             var _localeStrings = {};
18293
18294             // the current locale parameters
18295             var _localeCode = 'en-US';
18296             var _languageCode = 'en';
18297             var _textDirection = 'ltr';
18298             var _usesMetric = false;
18299             var _languageNames = {};
18300             var _scriptNames = {};
18301
18302             // getters for the current locale parameters
18303             localizer.localeCode = function () { return _localeCode; };
18304             localizer.languageCode = function () { return _languageCode; };
18305             localizer.textDirection = function () { return _textDirection; };
18306             localizer.usesMetric = function () { return _usesMetric; };
18307             localizer.languageNames = function () { return _languageNames; };
18308             localizer.scriptNames = function () { return _scriptNames; };
18309
18310
18311             // The client app may want to manually set the locale, regardless of the
18312             // settings provided by the browser
18313             var _preferredLocaleCodes = [];
18314             localizer.preferredLocaleCodes = function(codes) {
18315                 if (!arguments.length) { return _preferredLocaleCodes; }
18316                 if (typeof codes === 'string') {
18317                     // be generous and accept delimited strings as input
18318                     _preferredLocaleCodes = codes.split(/,|;| /gi).filter(Boolean);
18319                 } else {
18320                     _preferredLocaleCodes = codes;
18321                 }
18322                 return localizer;
18323             };
18324
18325
18326             var _loadPromise;
18327
18328             localizer.ensureLoaded = function () {
18329
18330                 if (_loadPromise) { return _loadPromise; }
18331
18332                 return _loadPromise = Promise.all([
18333                         // load the list of languages
18334                         _mainFileFetcher.get('languages'),
18335                         // load the list of supported locales
18336                         _mainFileFetcher.get('locales')
18337                     ])
18338                     .then(function (results) {
18339                         _dataLanguages = results[0];
18340                         _dataLocales = results[1];
18341                     })
18342                     .then(function () {
18343                         var requestedLocales = (_preferredLocaleCodes || [])
18344                             // list of locales preferred by the browser in priority order
18345                             .concat(utilDetect().browserLocales);
18346                         _localeCode = bestSupportedLocale(requestedLocales);
18347
18348                         return Promise.all([
18349                             // always load the English locale strings as fallbacks
18350                             localizer.loadLocale('en'),
18351                             // load the preferred locale
18352                             localizer.loadLocale(_localeCode)
18353                         ]);
18354                     })
18355                     .then(function () {
18356                         updateForCurrentLocale();
18357                     })
18358                     .catch(function (err) { return console.error(err); });  // eslint-disable-line
18359             };
18360
18361             // Returns the best locale from `locales` supported by iD, if any
18362             function bestSupportedLocale(locales) {
18363                 var supportedLocales = _dataLocales;
18364
18365                 var loop = function ( i ) {
18366                     var locale = locales[i];
18367                     if (locale.includes('-')) { // full locale ('es-ES')
18368
18369                         if (supportedLocales[locale]) { return { v: locale }; }
18370
18371                         // If full locale not supported ('es-FAKE'), fallback to the base ('es')
18372                         var langPart = locale.split('-')[0];
18373                         if (supportedLocales[langPart]) { return { v: langPart }; }
18374
18375                     } else { // base locale ('es')
18376
18377                         // prefer a lower-priority full locale with this base ('es' < 'es-ES')
18378                         var fullLocale = locales.find(function (locale2, index) {
18379                             return index > i &&
18380                                 locale2 !== locale &&
18381                                 locale2.split('-')[0] === locale &&
18382                                 supportedLocales[locale2];
18383                         });
18384                         if (fullLocale) { return { v: fullLocale }; }
18385
18386                         if (supportedLocales[locale]) { return { v: locale }; }
18387                     }
18388                 };
18389
18390                 for (var i in locales) {
18391                     var returned = loop( i );
18392
18393                     if ( returned ) return returned.v;
18394                 }
18395
18396                 return null;
18397             }
18398
18399             function updateForCurrentLocale() {
18400                 if (!_localeCode) { return; }
18401
18402                 _languageCode = _localeCode.split('-')[0];
18403
18404                 var currentData = _dataLocales[_localeCode] || _dataLocales[_languageCode];
18405
18406                 var hash = utilStringQs(window.location.hash);
18407
18408                 if (hash.rtl === 'true') {
18409                     _textDirection = 'rtl';
18410                 } else if (hash.rtl === 'false') {
18411                     _textDirection = 'ltr';
18412                 }  else {
18413                     _textDirection = currentData && currentData.rtl ? 'rtl' : 'ltr';
18414                 }
18415
18416                 _languageNames = currentData && currentData.languageNames;
18417                 _scriptNames = currentData && currentData.scriptNames;
18418
18419                 _usesMetric = _localeCode.slice(-3).toLowerCase() !== '-us';
18420             }
18421
18422
18423             /* Locales */
18424             // Returns a Promise to load the strings for the requested locale
18425             localizer.loadLocale = function (requested) {
18426
18427                 if (!_dataLocales) {
18428                     return Promise.reject('loadLocale called before init');
18429                 }
18430
18431                 var locale = requested;
18432
18433                 // US English is the default
18434                 if (locale.toLowerCase() === 'en-us') { locale = 'en'; }
18435
18436                 if (!_dataLocales[locale]) {
18437                     return Promise.reject(("Unsupported locale: " + requested));
18438                 }
18439
18440                 if (_localeStrings[locale]) {    // already loaded
18441                     return Promise.resolve(locale);
18442                 }
18443
18444                 var fileMap = _mainFileFetcher.fileMap();
18445                 var key = "locale_" + locale;
18446                 fileMap[key] = "locales/" + locale + ".json";
18447
18448                 return _mainFileFetcher.get(key)
18449                     .then(function (d) {
18450                         _localeStrings[locale] = d[locale];
18451                         return locale;
18452                     });
18453             };
18454
18455             /**
18456             * Given a string identifier, try to find that string in the current
18457             * language, and return it.  This function will be called recursively
18458             * with locale `en` if a string can not be found in the requested language.
18459             *
18460             * @param  {string}   s             string identifier
18461             * @param  {object?}  replacements  token replacements and default string
18462             * @param  {string?}  locale        locale to use (defaults to currentLocale)
18463             * @return {string?}  localized string
18464             */
18465             localizer.t = function(s, replacements, locale) {
18466                 locale = locale || _localeCode;
18467
18468                 // US English is the default
18469                 if (locale.toLowerCase() === 'en-us') { locale = 'en'; }
18470
18471                 var path = s
18472                   .split('.')
18473                   .map(function (s) { return s.replace(/<TX_DOT>/g, '.'); })
18474                   .reverse();
18475
18476                 var result = _localeStrings[locale];
18477
18478                 while (result !== undefined && path.length) {
18479                   result = result[path.pop()];
18480                 }
18481
18482                 if (result !== undefined) {
18483                   if (replacements) {
18484                     for (var k in replacements) {
18485                       var token = "{" + k + "}";
18486                       var regex = new RegExp(token, 'g');
18487                       result = result.replace(regex, replacements[k]);
18488                     }
18489                   }
18490                   return result;
18491                 }
18492
18493                 if (locale !== 'en') {
18494                   return localizer.t(s, replacements, 'en');  // fallback - recurse with 'en'
18495                 }
18496
18497                 if (replacements && 'default' in replacements) {
18498                   return replacements.default;      // fallback - replacements.default
18499                 }
18500
18501                 var missing = "Missing " + locale + " translation: " + s;
18502                 if (typeof console !== 'undefined') { console.error(missing); }  // eslint-disable-line
18503
18504                 return missing;
18505             };
18506
18507             localizer.languageName = function (code, options) {
18508
18509                 if (_languageNames[code]) {  // name in locale language
18510                   // e.g. "German"
18511                   return _languageNames[code];
18512                 }
18513
18514                 // sometimes we only want the local name
18515                 if (options && options.localOnly) { return null; }
18516
18517                 var langInfo = _dataLanguages[code];
18518                 if (langInfo) {
18519                   if (langInfo.nativeName) {  // name in native language
18520                     // e.g. "Deutsch (de)"
18521                     return localizer.t('translate.language_and_code', { language: langInfo.nativeName, code: code });
18522
18523                   } else if (langInfo.base && langInfo.script) {
18524                     var base = langInfo.base;   // the code of the language this is based on
18525
18526                     if (_languageNames[base]) {   // base language name in locale language
18527                       var scriptCode = langInfo.script;
18528                       var script = _scriptNames[scriptCode] || scriptCode;
18529                       // e.g. "Serbian (Cyrillic)"
18530                       return localizer.t('translate.language_and_code', { language: _languageNames[base], code: script });
18531
18532                     } else if (_dataLanguages[base] && _dataLanguages[base].nativeName) {
18533                       // e.g. "српски (sr-Cyrl)"
18534                       return localizer.t('translate.language_and_code', { language: _dataLanguages[base].nativeName, code: code });
18535                     }
18536                   }
18537                 }
18538                 return code;  // if not found, use the code
18539             };
18540
18541             return localizer;
18542         }
18543
18544         //
18545         // `presetCollection` is a wrapper around an `Array` of presets `collection`,
18546         // and decorated with some extra methods for searching and matching geometry
18547         //
18548         function presetCollection(collection) {
18549           var MAXRESULTS = 50;
18550           var _this = {};
18551           var _memo = {};
18552
18553           _this.collection = collection;
18554
18555           _this.item = function (id) {
18556             if (_memo[id]) { return _memo[id]; }
18557             var found = _this.collection.find(function (d) { return d.id === id; });
18558             if (found) { _memo[id] = found; }
18559             return found;
18560           };
18561
18562           _this.index = function (id) { return _this.collection.findIndex(function (d) { return d.id === id; }); };
18563
18564           _this.matchGeometry = function (geometry) {
18565             return presetCollection(
18566               _this.collection.filter(function (d) { return d.matchGeometry(geometry); })
18567             );
18568           };
18569
18570           _this.matchAllGeometry = function (geometries) {
18571             return presetCollection(
18572               _this.collection.filter(function (d) { return d && d.matchAllGeometry(geometries); })
18573             );
18574           };
18575
18576           _this.matchAnyGeometry = function (geometries) {
18577             return presetCollection(
18578               _this.collection.filter(function (d) { return geometries.some(function (geom) { return d.matchGeometry(geom); }); })
18579             );
18580           };
18581
18582           _this.fallback = function (geometry) {
18583             var id = geometry;
18584             if (id === 'vertex') { id = 'point'; }
18585             return _this.item(id);
18586           };
18587
18588           _this.search = function (value, geometry, countryCode) {
18589             if (!value) { return _this; }
18590
18591             value = value.toLowerCase().trim();
18592
18593             // match at name beginning or just after a space (e.g. "office" -> match "Law Office")
18594             function leading(a) {
18595               var index = a.indexOf(value);
18596               return index === 0 || a[index - 1] === ' ';
18597             }
18598
18599             // match at name beginning only
18600             function leadingStrict(a) {
18601               var index = a.indexOf(value);
18602               return index === 0;
18603             }
18604
18605             function sortNames(a, b) {
18606               var aCompare = (a.suggestion ? a.originalName : a.name()).toLowerCase();
18607               var bCompare = (b.suggestion ? b.originalName : b.name()).toLowerCase();
18608
18609               // priority if search string matches preset name exactly - #4325
18610               if (value === aCompare) { return -1; }
18611               if (value === bCompare) { return 1; }
18612
18613               // priority for higher matchScore
18614               var i = b.originalScore - a.originalScore;
18615               if (i !== 0) { return i; }
18616
18617               // priority if search string appears earlier in preset name
18618               i = aCompare.indexOf(value) - bCompare.indexOf(value);
18619               if (i !== 0) { return i; }
18620
18621               // priority for shorter preset names
18622               return aCompare.length - bCompare.length;
18623             }
18624
18625             var pool = _this.collection;
18626             if (countryCode) {
18627               pool = pool.filter(function (a) {
18628                 if (a.countryCodes && a.countryCodes.indexOf(countryCode) === -1) { return false; }
18629                 if (a.notCountryCodes && a.notCountryCodes.indexOf(countryCode) !== -1) { return false; }
18630                 return true;
18631               });
18632             }
18633             var searchable = pool.filter(function (a) { return a.searchable !== false && a.suggestion !== true; });
18634             var suggestions = pool.filter(function (a) { return a.suggestion === true; });
18635
18636             // matches value to preset.name
18637             var leading_name = searchable
18638               .filter(function (a) { return leading(a.name().toLowerCase()); })
18639               .sort(sortNames);
18640
18641             // matches value to preset suggestion name (original name is unhyphenated)
18642             var leading_suggestions = suggestions
18643               .filter(function (a) { return leadingStrict(a.originalName.toLowerCase()); })
18644               .sort(sortNames);
18645
18646             // matches value to preset.terms values
18647             var leading_terms = searchable
18648               .filter(function (a) { return (a.terms() || []).some(leading); });
18649
18650             // matches value to preset.tags values
18651             var leading_tag_values = searchable
18652               .filter(function (a) { return Object.values(a.tags || {}).filter(function (val) { return val !== '*'; }).some(leading); });
18653
18654             // finds close matches to value in preset.name
18655             var similar_name = searchable
18656               .map(function (a) { return ({ preset: a, dist: utilEditDistance(value, a.name()) }); })
18657               .filter(function (a) { return a.dist + Math.min(value.length - a.preset.name().length, 0) < 3; })
18658               .sort(function (a, b) { return a.dist - b.dist; })
18659               .map(function (a) { return a.preset; });
18660
18661             // finds close matches to value to preset suggestion name (original name is unhyphenated)
18662             var similar_suggestions = suggestions
18663               .map(function (a) { return ({ preset: a, dist: utilEditDistance(value, a.originalName.toLowerCase()) }); })
18664               .filter(function (a) { return a.dist + Math.min(value.length - a.preset.originalName.length, 0) < 1; })
18665               .sort(function (a, b) { return a.dist - b.dist; })
18666               .map(function (a) { return a.preset; });
18667
18668             // finds close matches to value in preset.terms
18669             var similar_terms = searchable
18670               .filter(function (a) {
18671                 return (a.terms() || []).some(function (b) {
18672                   return utilEditDistance(value, b) + Math.min(value.length - b.length, 0) < 3;
18673                 });
18674               });
18675
18676             var results = leading_name.concat(
18677               leading_suggestions,
18678               leading_terms,
18679               leading_tag_values,
18680               similar_name,
18681               similar_suggestions,
18682               similar_terms
18683             ).slice(0, MAXRESULTS - 1);
18684
18685             if (geometry) {
18686               if (typeof geometry === 'string') {
18687                 results.push(_this.fallback(geometry));
18688               } else {
18689                 geometry.forEach(function (geom) { return results.push(_this.fallback(geom)); });
18690               }
18691             }
18692
18693             return presetCollection(utilArrayUniq(results));
18694           };
18695
18696
18697           return _this;
18698         }
18699
18700         //
18701         // `presetCategory` builds a `presetCollection` of member presets,
18702         // decorated with some extra methods for searching and matching geometry
18703         //
18704         function presetCategory(categoryID, category, all) {
18705           var _this = Object.assign({}, category);   // shallow copy
18706
18707           _this.id = categoryID;
18708
18709           _this.members = presetCollection(
18710             category.members.map(function (presetID) { return all.item(presetID); }).filter(Boolean)
18711           );
18712
18713           _this.geometry = _this.members.collection
18714             .reduce(function (acc, preset) {
18715               for (var i in preset.geometry) {
18716                 var geometry = preset.geometry[i];
18717                 if (acc.indexOf(geometry) === -1) {
18718                   acc.push(geometry);
18719                 }
18720               }
18721               return acc;
18722             }, []);
18723
18724           _this.matchGeometry = function (geom) { return _this.geometry.indexOf(geom) >= 0; };
18725
18726           _this.matchAllGeometry = function (geometries) { return _this.members.collection
18727             .some(function (preset) { return preset.matchAllGeometry(geometries); }); };
18728
18729           _this.matchScore = function () { return -1; };
18730
18731           _this.name = function () { return _t(("presets.categories." + categoryID + ".name"), { 'default': categoryID }); };
18732
18733           _this.terms = function () { return []; };
18734
18735
18736           return _this;
18737         }
18738
18739         //
18740         // `presetField` decorates a given `field` Object
18741         // with some extra methods for searching and matching geometry
18742         //
18743         function presetField(fieldID, field) {
18744           var _this = Object.assign({}, field);   // shallow copy
18745
18746           _this.id = fieldID;
18747
18748           // for use in classes, element ids, css selectors
18749           _this.safeid = utilSafeClassName(fieldID);
18750
18751           _this.matchGeometry = function (geom) { return !_this.geometry || _this.geometry.indexOf(geom) !== -1; };
18752
18753           _this.matchAllGeometry = function (geometries) {
18754             return !_this.geometry || geometries.every(function (geom) { return _this.geometry.indexOf(geom) !== -1; });
18755           };
18756
18757           _this.t = function (scope, options) { return _t(("presets.fields." + fieldID + "." + scope), options); };
18758
18759           _this.label = function () { return _this.overrideLabel || _this.t('label', { 'default': fieldID }); };
18760
18761           var _placeholder = _this.placeholder;
18762           _this.placeholder = function () { return _this.t('placeholder', { 'default': _placeholder }); };
18763
18764           _this.originalTerms = (_this.terms || []).join();
18765
18766           _this.terms = function () { return _this.t('terms', { 'default': _this.originalTerms })
18767             .toLowerCase().trim().split(/\s*,+\s*/); };
18768
18769
18770           return _this;
18771         }
18772
18773         //
18774         // `presetPreset` decorates a given `preset` Object
18775         // with some extra methods for searching and matching geometry
18776         //
18777         function presetPreset(presetID, preset, addable, allFields, allPresets) {
18778           allFields = allFields || {};
18779           allPresets = allPresets || {};
18780           var _this = Object.assign({}, preset);   // shallow copy
18781           var _addable = addable || false;
18782           var _resolvedFields;      // cache
18783           var _resolvedMoreFields;  // cache
18784
18785           _this.id = presetID;
18786
18787           _this.safeid = utilSafeClassName(presetID);  // for use in css classes, selectors, element ids
18788
18789           _this.originalTerms = (_this.terms || []).join();
18790
18791           _this.originalName = _this.name || '';
18792
18793           _this.originalScore = _this.matchScore || 1;
18794
18795           _this.originalReference = _this.reference || {};
18796
18797           _this.originalFields = (_this.fields || []);
18798
18799           _this.originalMoreFields = (_this.moreFields || []);
18800
18801           _this.fields = function () { return _resolvedFields || (_resolvedFields = resolve('fields')); };
18802
18803           _this.moreFields = function () { return _resolvedMoreFields || (_resolvedMoreFields = resolve('moreFields')); };
18804
18805           _this.resetFields = function () { return _resolvedFields = _resolvedMoreFields = null; };
18806
18807           _this.tags = _this.tags || {};
18808
18809           _this.addTags = _this.addTags || _this.tags;
18810
18811           _this.removeTags = _this.removeTags || _this.addTags;
18812
18813           _this.geometry = (_this.geometry || []);
18814
18815           _this.matchGeometry = function (geom) { return _this.geometry.indexOf(geom) >= 0; };
18816
18817           _this.matchAllGeometry = function (geoms) { return geoms.every(_this.matchGeometry); };
18818
18819           _this.matchScore = function (entityTags) {
18820             var tags = _this.tags;
18821             var seen = {};
18822             var score = 0;
18823
18824             // match on tags
18825             for (var k in tags) {
18826               seen[k] = true;
18827               if (entityTags[k] === tags[k]) {
18828                 score += _this.originalScore;
18829               } else if (tags[k] === '*' && k in entityTags) {
18830                 score += _this.originalScore / 2;
18831               } else {
18832                 return -1;
18833               }
18834             }
18835
18836             // boost score for additional matches in addTags - #6802
18837             var addTags = _this.addTags;
18838             for (var k$1 in addTags) {
18839               if (!seen[k$1] && entityTags[k$1] === addTags[k$1]) {
18840                 score += _this.originalScore;
18841               }
18842             }
18843
18844             return score;
18845           };
18846
18847
18848           var _textCache = {};
18849           _this.t = function (scope, options) {
18850             var textID = "presets.presets." + presetID + "." + scope;
18851             if (_textCache[textID]) { return _textCache[textID]; }
18852             return _textCache[textID] = _t(textID, options);
18853           };
18854
18855
18856           _this.name = function () {
18857             if (_this.suggestion) {
18858               var path = presetID.split('/');
18859               path.pop();  // remove brand name
18860               // NOTE: insert an en-dash, not a hyphen (to avoid conflict with fr - nl names in Brussels etc)
18861               return _this.originalName + ' – ' + _t('presets.presets.' + path.join('/') + '.name');
18862             }
18863             return _this.t('name', { 'default': _this.originalName });
18864           };
18865
18866
18867           _this.terms = function () { return _this.t('terms', { 'default': _this.originalTerms })
18868             .toLowerCase().trim().split(/\s*,+\s*/); };
18869
18870
18871           _this.isFallback = function () {
18872             var tagCount = Object.keys(_this.tags).length;
18873             return tagCount === 0 || (tagCount === 1 && _this.tags.hasOwnProperty('area'));
18874           };
18875
18876
18877           _this.addable = function(val) {
18878             if (!arguments.length) { return _addable; }
18879             _addable = val;
18880             return _this;
18881           };
18882
18883
18884           _this.reference = function () {
18885             // Lookup documentation on Wikidata...
18886             var qid = _this.tags.wikidata || _this.tags['brand:wikidata'] || _this.tags['operator:wikidata'];
18887             if (qid) {
18888               return { qid: qid };
18889             }
18890
18891             // Lookup documentation on OSM Wikibase...
18892             var key = _this.originalReference.key || Object.keys(utilObjectOmit(_this.tags, 'name'))[0];
18893             var value = _this.originalReference.value || _this.tags[key];
18894
18895             if (value === '*') {
18896               return { key: key };
18897             } else {
18898               return { key: key, value: value };
18899             }
18900           };
18901
18902
18903           _this.unsetTags = function (tags, geometry, skipFieldDefaults) {
18904             tags = utilObjectOmit(tags, Object.keys(_this.removeTags));
18905
18906             if (geometry && !skipFieldDefaults) {
18907               _this.fields().forEach(function (field) {
18908                 if (field.matchGeometry(geometry) && field.key && field.default === tags[field.key]) {
18909                   delete tags[field.key];
18910                 }
18911               });
18912             }
18913
18914             delete tags.area;
18915             return tags;
18916           };
18917
18918
18919           _this.setTags = function (tags, geometry, skipFieldDefaults) {
18920             var addTags = _this.addTags;
18921             tags = Object.assign({}, tags);   // shallow copy
18922
18923             for (var k in addTags) {
18924               if (addTags[k] === '*') {
18925                 tags[k] = 'yes';
18926               } else {
18927                 tags[k] = addTags[k];
18928               }
18929             }
18930
18931             // Add area=yes if necessary.
18932             // This is necessary if the geometry is already an area (e.g. user drew an area) AND any of:
18933             // 1. chosen preset could be either an area or a line (`barrier=city_wall`)
18934             // 2. chosen preset doesn't have a key in osmAreaKeys (`railway=station`)
18935             if (!addTags.hasOwnProperty('area')) {
18936               delete tags.area;
18937               if (geometry === 'area') {
18938                 var needsAreaTag = true;
18939                 if (_this.geometry.indexOf('line') === -1) {
18940                   for (var k$1 in addTags) {
18941                     if (k$1 in osmAreaKeys) {
18942                       needsAreaTag = false;
18943                       break;
18944                     }
18945                   }
18946                 }
18947                 if (needsAreaTag) {
18948                   tags.area = 'yes';
18949                 }
18950               }
18951             }
18952
18953             if (geometry && !skipFieldDefaults) {
18954               _this.fields().forEach(function (field) {
18955                 if (field.matchGeometry(geometry) && field.key && !tags[field.key] && field.default) {
18956                   tags[field.key] = field.default;
18957                 }
18958               });
18959             }
18960
18961             return tags;
18962           };
18963
18964
18965           // For a preset without fields, use the fields of the parent preset.
18966           // Replace {preset} placeholders with the fields of the specified presets.
18967           function resolve(which) {
18968             var fieldIDs = (which === 'fields' ? _this.originalFields : _this.originalMoreFields);
18969             var resolved = [];
18970
18971             fieldIDs.forEach(function (fieldID) {
18972               var match = fieldID.match(/\{(.*)\}/);
18973               if (match !== null) {    // a presetID wrapped in braces {}
18974                 resolved = resolved.concat(inheritFields(match[1], which));
18975               } else if (allFields[fieldID]) {    // a normal fieldID
18976                 resolved.push(allFields[fieldID]);
18977               } else {
18978                 console.log(("Cannot resolve \"" + fieldID + "\" found in " + (_this.id) + "." + which));  // eslint-disable-line no-console
18979               }
18980             });
18981
18982             // no fields resolved, so use the parent's if possible
18983             if (!resolved.length) {
18984               var endIndex = _this.id.lastIndexOf('/');
18985               var parentID = endIndex && _this.id.substring(0, endIndex);
18986               if (parentID) {
18987                 resolved = inheritFields(parentID, which);
18988               }
18989             }
18990
18991             return utilArrayUniq(resolved);
18992
18993
18994             // returns an array of fields to inherit from the given presetID, if found
18995             function inheritFields(presetID, which) {
18996               var parent = allPresets[presetID];
18997               if (!parent) { return []; }
18998
18999               if (which === 'fields') {
19000                 return parent.fields().filter(shouldInherit);
19001               } else if (which === 'moreFields') {
19002                 return parent.moreFields();
19003               } else {
19004                 return [];
19005               }
19006             }
19007
19008
19009             // Skip `fields` for the keys which define the preset.
19010             // These are usually `typeCombo` fields like `shop=*`
19011             function shouldInherit(f) {
19012               if (f.key && _this.tags[f.key] !== undefined &&
19013                 // inherit anyway if multiple values are allowed or just a checkbox
19014                 f.type !== 'multiCombo' && f.type !== 'semiCombo' && f.type !== 'check'
19015               ) { return false; }
19016
19017               return true;
19018             }
19019           }
19020
19021
19022           return _this;
19023         }
19024
19025         var _mainPresetIndex = presetIndex(); // singleton
19026
19027         //
19028         // `presetIndex` wraps a `presetCollection`
19029         // with methods for loading new data and returning defaults
19030         //
19031         function presetIndex() {
19032           var dispatch$1 = dispatch('favoritePreset', 'recentsChange');
19033           var MAXRECENTS = 30;
19034
19035           // seed the preset lists with geometry fallbacks
19036           var POINT = presetPreset('point', { name: 'Point', tags: {}, geometry: ['point', 'vertex'], matchScore: 0.1 } );
19037           var LINE = presetPreset('line', { name: 'Line', tags: {}, geometry: ['line'], matchScore: 0.1 } );
19038           var AREA = presetPreset('area', { name: 'Area', tags: { area: 'yes' }, geometry: ['area'], matchScore: 0.1 } );
19039           var RELATION = presetPreset('relation', { name: 'Relation', tags: {}, geometry: ['relation'], matchScore: 0.1 } );
19040
19041           var _this = presetCollection([POINT, LINE, AREA, RELATION]);
19042           var _presets = { point: POINT, line: LINE, area: AREA, relation: RELATION };
19043
19044           var _defaults = {
19045             point: presetCollection([POINT]),
19046             vertex: presetCollection([POINT]),
19047             line: presetCollection([LINE]),
19048             area: presetCollection([AREA]),
19049             relation: presetCollection([RELATION])
19050           };
19051
19052           var _fields = {};
19053           var _categories = {};
19054           var _universal = [];
19055           var _addablePresetIDs = null;   // Set of preset IDs that the user can add
19056           var _recents;
19057           var _favorites;
19058
19059           // Index of presets by (geometry, tag key).
19060           var _geometryIndex = { point: {}, vertex: {}, line: {}, area: {}, relation: {} };
19061
19062           var _loadPromise;
19063
19064           _this.ensureLoaded = function () {
19065             if (_loadPromise) { return _loadPromise; }
19066
19067             return _loadPromise = Promise.all([
19068                 _mainFileFetcher.get('preset_categories'),
19069                 _mainFileFetcher.get('preset_defaults'),
19070                 _mainFileFetcher.get('preset_presets'),
19071                 _mainFileFetcher.get('preset_fields')
19072               ])
19073               .then(function (vals) {
19074                 _this.merge({
19075                   categories: vals[0],
19076                   defaults: vals[1],
19077                   presets: vals[2],
19078                   fields: vals[3]
19079                 });
19080                 osmSetAreaKeys(_this.areaKeys());
19081                 osmSetPointTags(_this.pointTags());
19082                 osmSetVertexTags(_this.vertexTags());
19083               });
19084           };
19085
19086
19087           _this.merge = function (d) {
19088             // Merge Fields
19089             if (d.fields) {
19090               Object.keys(d.fields).forEach(function (fieldID) {
19091                 var f = d.fields[fieldID];
19092                 if (f) {   // add or replace
19093                   _fields[fieldID] = presetField(fieldID, f);
19094                 } else {   // remove
19095                   delete _fields[fieldID];
19096                 }
19097               });
19098             }
19099
19100             // Merge Presets
19101             if (d.presets) {
19102               Object.keys(d.presets).forEach(function (presetID) {
19103                 var p = d.presets[presetID];
19104                 if (p) {   // add or replace
19105                   var isAddable = !_addablePresetIDs || _addablePresetIDs.has(presetID);
19106                   _presets[presetID] = presetPreset(presetID, p, isAddable, _fields, _presets);
19107                 } else {   // remove (but not if it's a fallback)
19108                   var existing = _presets[presetID];
19109                   if (existing && !existing.isFallback()) {
19110                     delete _presets[presetID];
19111                   }
19112                 }
19113               });
19114             }
19115
19116             // Need to rebuild _this.collection before loading categories
19117             _this.collection = Object.values(_presets).concat(Object.values(_categories));
19118
19119             // Merge Categories
19120             if (d.categories) {
19121               Object.keys(d.categories).forEach(function (categoryID) {
19122                 var c = d.categories[categoryID];
19123                 if (c) {   // add or replace
19124                   _categories[categoryID] = presetCategory(categoryID, c, _this);
19125                 } else {   // remove
19126                   delete _categories[categoryID];
19127                 }
19128               });
19129             }
19130
19131             // Rebuild _this.collection after loading categories
19132             _this.collection = Object.values(_presets).concat(Object.values(_categories));
19133
19134             // Merge Defaults
19135             if (d.defaults) {
19136               Object.keys(d.defaults).forEach(function (geometry) {
19137                 var def = d.defaults[geometry];
19138                 if (Array.isArray(def)) {   // add or replace
19139                   _defaults[geometry] = presetCollection(
19140                     def.map(function (id) { return _presets[id] || _categories[id]; }).filter(Boolean)
19141                   );
19142                 } else {   // remove
19143                   delete _defaults[geometry];
19144                 }
19145               });
19146             }
19147
19148             // Rebuild universal fields array
19149             _universal = Object.values(_fields).filter(function (field) { return field.universal; });
19150
19151             // Reset all the preset fields - they'll need to be resolved again
19152             Object.values(_presets).forEach(function (preset) { return preset.resetFields(); });
19153
19154             // Rebuild geometry index
19155             _geometryIndex = { point: {}, vertex: {}, line: {}, area: {}, relation: {} };
19156             _this.collection.forEach(function (preset) {
19157               (preset.geometry || []).forEach(function (geometry) {
19158                 var g = _geometryIndex[geometry];
19159                 for (var key in preset.tags) {
19160                   (g[key] = g[key] || []).push(preset);
19161                 }
19162               });
19163             });
19164
19165             return _this;
19166           };
19167
19168
19169           _this.match = function (entity, resolver) {
19170             return resolver.transient(entity, 'presetMatch', function () {
19171               var geometry = entity.geometry(resolver);
19172               // Treat entities on addr:interpolation lines as points, not vertices - #3241
19173               if (geometry === 'vertex' && entity.isOnAddressLine(resolver)) {
19174                 geometry = 'point';
19175               }
19176               return _this.matchTags(entity.tags, geometry);
19177             });
19178           };
19179
19180
19181           _this.matchTags = function (tags, geometry) {
19182             var geometryMatches = _geometryIndex[geometry];
19183             var address;
19184             var best = -1;
19185             var match;
19186
19187             for (var k in tags) {
19188               // If any part of an address is present, allow fallback to "Address" preset - #4353
19189               if (/^addr:/.test(k) && geometryMatches['addr:*']) {
19190                 address = geometryMatches['addr:*'][0];
19191               }
19192
19193               var keyMatches = geometryMatches[k];
19194               if (!keyMatches) { continue; }
19195
19196               for (var i = 0; i < keyMatches.length; i++) {
19197                 var score = keyMatches[i].matchScore(tags);
19198                 if (score > best) {
19199                   best = score;
19200                   match = keyMatches[i];
19201                 }
19202               }
19203             }
19204
19205             if (address && (!match || match.isFallback())) {
19206               match = address;
19207             }
19208             return match || _this.fallback(geometry);
19209           };
19210
19211
19212           _this.allowsVertex = function (entity, resolver) {
19213             if (entity.type !== 'node') { return false; }
19214             if (Object.keys(entity.tags).length === 0) { return true; }
19215
19216             return resolver.transient(entity, 'vertexMatch', function () {
19217               // address lines allow vertices to act as standalone points
19218               if (entity.isOnAddressLine(resolver)) { return true; }
19219
19220               var geometries = osmNodeGeometriesForTags(entity.tags);
19221               if (geometries.vertex) { return true; }
19222               if (geometries.point) { return false; }
19223               // allow vertices for unspecified points
19224               return true;
19225             });
19226           };
19227
19228
19229           // Because of the open nature of tagging, iD will never have a complete
19230           // list of tags used in OSM, so we want it to have logic like "assume
19231           // that a closed way with an amenity tag is an area, unless the amenity
19232           // is one of these specific types". This function computes a structure
19233           // that allows testing of such conditions, based on the presets designated
19234           // as as supporting (or not supporting) the area geometry.
19235           //
19236           // The returned object L is a keeplist/discardlist of tags. A closed way
19237           // with a tag (k, v) is considered to be an area if `k in L && !(v in L[k])`
19238           // (see `Way#isArea()`). In other words, the keys of L form the keeplist,
19239           // and the subkeys form the discardlist.
19240           _this.areaKeys = function () {
19241             // The ignore list is for keys that imply lines. (We always add `area=yes` for exceptions)
19242             var ignore = ['barrier', 'highway', 'footway', 'railway', 'junction', 'type'];
19243             var areaKeys = {};
19244
19245             // ignore name-suggestion-index and deprecated presets
19246             var presets = _this.collection.filter(function (p) { return !p.suggestion && !p.replacement; });
19247
19248             // keeplist
19249             presets.forEach(function (p) {
19250               var key;
19251               for (key in p.tags) { break; }  // pick the first tag
19252               if (!key) { return; }
19253               if (ignore.indexOf(key) !== -1) { return; }
19254
19255               if (p.geometry.indexOf('area') !== -1) {    // probably an area..
19256                 areaKeys[key] = areaKeys[key] || {};
19257               }
19258             });
19259
19260             // discardlist
19261             presets.forEach(function (p) {
19262               var key;
19263               for (key in p.addTags) {
19264                 // examine all addTags to get a better sense of what can be tagged on lines - #6800
19265                 var value = p.addTags[key];
19266                 if (key in areaKeys &&                    // probably an area...
19267                   p.geometry.indexOf('line') !== -1 &&    // but sometimes a line
19268                   value !== '*') {
19269                   areaKeys[key][value] = true;
19270                 }
19271               }
19272             });
19273
19274             return areaKeys;
19275           };
19276
19277
19278           _this.pointTags = function () {
19279             return _this.collection.reduce(function (pointTags, d) {
19280               // ignore name-suggestion-index, deprecated, and generic presets
19281               if (d.suggestion || d.replacement || d.searchable === false) { return pointTags; }
19282
19283               // only care about the primary tag
19284               var key;
19285               for (key in d.tags) { break; }  // pick the first tag
19286               if (!key) { return pointTags; }
19287
19288               // if this can be a point
19289               if (d.geometry.indexOf('point') !== -1) {
19290                 pointTags[key] = pointTags[key] || {};
19291                 pointTags[key][d.tags[key]] = true;
19292               }
19293               return pointTags;
19294             }, {});
19295           };
19296
19297
19298           _this.vertexTags = function () {
19299             return _this.collection.reduce(function (vertexTags, d) {
19300               // ignore name-suggestion-index, deprecated, and generic presets
19301               if (d.suggestion || d.replacement || d.searchable === false) { return vertexTags; }
19302
19303               // only care about the primary tag
19304               var key;
19305               for (key in d.tags) { break; }   // pick the first tag
19306               if (!key) { return vertexTags; }
19307
19308               // if this can be a vertex
19309               if (d.geometry.indexOf('vertex') !== -1) {
19310                 vertexTags[key] = vertexTags[key] || {};
19311                 vertexTags[key][d.tags[key]] = true;
19312               }
19313               return vertexTags;
19314             }, {});
19315           };
19316
19317
19318           _this.field = function (id) { return _fields[id]; };
19319
19320           _this.universal = function () { return _universal; };
19321
19322
19323           _this.defaults = function (geometry, n, startWithRecents) {
19324             var recents = [];
19325             if (startWithRecents) {
19326               recents = _this.recent().matchGeometry(geometry).collection.slice(0, 4);
19327             }
19328             var defaults;
19329             if (_addablePresetIDs) {
19330               defaults = Array.from(_addablePresetIDs).map(function(id) {
19331                 var preset = _this.item(id);
19332                 if (preset && preset.matchGeometry(geometry)) { return preset; }
19333                 return null;
19334               }).filter(Boolean);
19335             } else {
19336               defaults = _defaults[geometry].collection.concat(_this.fallback(geometry));
19337             }
19338
19339             return presetCollection(
19340               utilArrayUniq(recents.concat(defaults)).slice(0, n - 1)
19341             );
19342           };
19343
19344           // pass a Set of addable preset ids
19345           _this.addablePresetIDs = function(val) {
19346             if (!arguments.length) { return _addablePresetIDs; }
19347
19348             // accept and convert arrays
19349             if (Array.isArray(val)) { val = new Set(val); }
19350
19351             _addablePresetIDs = val;
19352             if (_addablePresetIDs) {   // reset all presets
19353               _this.collection.forEach(function (p) {
19354                 // categories aren't addable
19355                 if (p.addable) { p.addable(_addablePresetIDs.has(p.id)); }
19356               });
19357             } else {
19358               _this.collection.forEach(function (p) {
19359                 if (p.addable) { p.addable(true); }
19360               });
19361             }
19362
19363             return _this;
19364           };
19365
19366
19367           _this.recent = function () {
19368             return presetCollection(
19369               utilArrayUniq(_this.getRecents().map(function (d) { return d.preset; }))
19370             );
19371           };
19372
19373
19374           function RibbonItem(preset, source) {
19375             var item = {};
19376             item.preset = preset;
19377             item.source = source;
19378
19379             item.isFavorite = function () { return item.source === 'favorite'; };
19380             item.isRecent = function () { return item.source === 'recent'; };
19381             item.matches = function (preset) { return item.preset.id === preset.id; };
19382             item.minified = function () { return ({ pID: item.preset.id }); };
19383
19384             return item;
19385           }
19386
19387
19388           function ribbonItemForMinified(d, source) {
19389             if (d && d.pID) {
19390               var preset = _this.item(d.pID);
19391               if (!preset) { return null; }
19392               return RibbonItem(preset, source);
19393             }
19394             return null;
19395           }
19396
19397
19398           _this.getGenericRibbonItems = function () {
19399             return ['point', 'line', 'area'].map(function (id) { return RibbonItem(_this.item(id), 'generic'); });
19400           };
19401
19402
19403           _this.getAddable = function () {
19404               if (!_addablePresetIDs) { return []; }
19405
19406               return _addablePresetIDs.map(function (id) {
19407                 var preset = _this.item(id);
19408                 if (preset) {
19409                   return RibbonItem(preset, 'addable');
19410                 }
19411               }).filter(Boolean);
19412           };
19413
19414
19415           function setRecents(items) {
19416             _recents = items;
19417             var minifiedItems = items.map(function (d) { return d.minified(); });
19418             corePreferences('preset_recents', JSON.stringify(minifiedItems));
19419             dispatch$1.call('recentsChange');
19420           }
19421
19422
19423           _this.getRecents = function () {
19424             if (!_recents) {
19425               // fetch from local storage
19426               _recents = (JSON.parse(corePreferences('preset_recents')) || [])
19427                 .reduce(function (acc, d) {
19428                   var item = ribbonItemForMinified(d, 'recent');
19429                   if (item && item.preset.addable()) { acc.push(item); }
19430                   return acc;
19431                 }, []);
19432             }
19433             return _recents;
19434           };
19435
19436
19437           _this.addRecent = function (preset, besidePreset, after) {
19438             var recents = _this.getRecents();
19439
19440             var beforeItem = _this.recentMatching(besidePreset);
19441             var toIndex = recents.indexOf(beforeItem);
19442             if (after) { toIndex += 1; }
19443
19444             var newItem = RibbonItem(preset, 'recent');
19445             recents.splice(toIndex, 0, newItem);
19446             setRecents(recents);
19447           };
19448
19449
19450           _this.removeRecent = function (preset) {
19451             var item = _this.recentMatching(preset);
19452             if (item) {
19453               var items = _this.getRecents();
19454               items.splice(items.indexOf(item), 1);
19455               setRecents(items);
19456             }
19457           };
19458
19459
19460           _this.recentMatching = function (preset) {
19461             var items = _this.getRecents();
19462             for (var i in items) {
19463               if (items[i].matches(preset)) {
19464                 return items[i];
19465               }
19466             }
19467             return null;
19468           };
19469
19470
19471           _this.moveItem = function (items, fromIndex, toIndex) {
19472             if (fromIndex === toIndex ||
19473               fromIndex < 0 || toIndex < 0 ||
19474               fromIndex >= items.length || toIndex >= items.length
19475             ) { return null; }
19476
19477             items.splice(toIndex, 0, items.splice(fromIndex, 1)[0]);
19478             return items;
19479           };
19480
19481
19482           _this.moveRecent = function (item, beforeItem) {
19483             var recents = _this.getRecents();
19484             var fromIndex = recents.indexOf(item);
19485             var toIndex = recents.indexOf(beforeItem);
19486             var items = _this.moveItem(recents, fromIndex, toIndex);
19487             if (items) { setRecents(items); }
19488           };
19489
19490
19491           _this.setMostRecent = function (preset) {
19492             if (preset.searchable === false) { return; }
19493
19494             var items = _this.getRecents();
19495             var item = _this.recentMatching(preset);
19496             if (item) {
19497               items.splice(items.indexOf(item), 1);
19498             } else {
19499               item = RibbonItem(preset, 'recent');
19500             }
19501
19502             // remove the last recent (first in, first out)
19503             while (items.length >= MAXRECENTS) {
19504               items.pop();
19505             }
19506
19507             // prepend array
19508             items.unshift(item);
19509             setRecents(items);
19510           };
19511
19512           function setFavorites(items) {
19513             _favorites = items;
19514             var minifiedItems = items.map(function (d) { return d.minified(); });
19515             corePreferences('preset_favorites', JSON.stringify(minifiedItems));
19516
19517             // call update
19518             dispatch$1.call('favoritePreset');
19519           }
19520
19521           _this.addFavorite = function (preset, besidePreset, after) {
19522               var favorites = _this.getFavorites();
19523
19524               var beforeItem = _this.favoriteMatching(besidePreset);
19525               var toIndex = favorites.indexOf(beforeItem);
19526               if (after) { toIndex += 1; }
19527
19528               var newItem = RibbonItem(preset, 'favorite');
19529               favorites.splice(toIndex, 0, newItem);
19530               setFavorites(favorites);
19531           };
19532
19533           _this.toggleFavorite = function (preset) {
19534             var favs = _this.getFavorites();
19535             var favorite = _this.favoriteMatching(preset);
19536             if (favorite) {
19537               favs.splice(favs.indexOf(favorite), 1);
19538             } else {
19539               // only allow 10 favorites
19540               if (favs.length === 10) {
19541                   // remove the last favorite (last in, first out)
19542                   favs.pop();
19543               }
19544               // append array
19545               favs.push(RibbonItem(preset, 'favorite'));
19546             }
19547             setFavorites(favs);
19548           };
19549
19550
19551           _this.removeFavorite = function (preset) {
19552             var item = _this.favoriteMatching(preset);
19553             if (item) {
19554               var items = _this.getFavorites();
19555               items.splice(items.indexOf(item), 1);
19556               setFavorites(items);
19557             }
19558           };
19559
19560
19561           _this.getFavorites = function () {
19562             if (!_favorites) {
19563
19564               // fetch from local storage
19565               var rawFavorites = JSON.parse(corePreferences('preset_favorites'));
19566
19567               if (!rawFavorites) {
19568                 rawFavorites = [];
19569                 corePreferences('preset_favorites', JSON.stringify(rawFavorites));
19570               }
19571
19572               _favorites = rawFavorites.reduce(function (output, d) {
19573                 var item = ribbonItemForMinified(d, 'favorite');
19574                 if (item && item.preset.addable()) { output.push(item); }
19575                 return output;
19576               }, []);
19577             }
19578             return _favorites;
19579           };
19580
19581
19582           _this.favoriteMatching = function (preset) {
19583             var favs = _this.getFavorites();
19584             for (var index in favs) {
19585               if (favs[index].matches(preset)) {
19586                 return favs[index];
19587               }
19588             }
19589             return null;
19590           };
19591
19592
19593           return utilRebind(_this, dispatch$1, 'on');
19594         }
19595
19596         function utilTagText(entity) {
19597             var obj = (entity && entity.tags) || {};
19598             return Object.keys(obj)
19599                 .map(function(k) { return k + '=' + obj[k]; })
19600                 .join(', ');
19601         }
19602
19603
19604         function utilTotalExtent(array, graph) {
19605             var extent = geoExtent();
19606             var val, entity;
19607             for (var i = 0; i < array.length; i++) {
19608                 val = array[i];
19609                 entity = typeof val === 'string' ? graph.hasEntity(val) : val;
19610                 if (entity) {
19611                     extent._extend(entity.extent(graph));
19612                 }
19613             }
19614             return extent;
19615         }
19616
19617
19618         function utilTagDiff(oldTags, newTags) {
19619             var tagDiff = [];
19620             var keys = utilArrayUnion(Object.keys(oldTags), Object.keys(newTags)).sort();
19621             keys.forEach(function(k) {
19622                 var oldVal = oldTags[k];
19623                 var newVal = newTags[k];
19624
19625                 if ((oldVal || oldVal === '') && (newVal === undefined || newVal !== oldVal)) {
19626                     tagDiff.push({
19627                         type: '-',
19628                         key: k,
19629                         oldVal: oldVal,
19630                         newVal: newVal,
19631                         display: '- ' + k + '=' + oldVal
19632                     });
19633                 }
19634                 if ((newVal || newVal === '') && (oldVal === undefined || newVal !== oldVal)) {
19635                     tagDiff.push({
19636                         type: '+',
19637                         key: k,
19638                         oldVal: oldVal,
19639                         newVal: newVal,
19640                         display: '+ ' + k + '=' + newVal
19641                     });
19642                 }
19643             });
19644             return tagDiff;
19645         }
19646
19647
19648         function utilEntitySelector(ids) {
19649             return ids.length ? '.' + ids.join(',.') : 'nothing';
19650         }
19651
19652
19653         // returns an selector to select entity ids for:
19654         //  - entityIDs passed in
19655         //  - shallow descendant entityIDs for any of those entities that are relations
19656         function utilEntityOrMemberSelector(ids, graph) {
19657             var seen = new Set(ids);
19658             ids.forEach(collectShallowDescendants);
19659             return utilEntitySelector(Array.from(seen));
19660
19661             function collectShallowDescendants(id) {
19662                 var entity = graph.hasEntity(id);
19663                 if (!entity || entity.type !== 'relation') { return; }
19664
19665                 entity.members
19666                     .map(function(member) { return member.id; })
19667                     .forEach(function(id) { seen.add(id); });
19668             }
19669         }
19670
19671
19672         // returns an selector to select entity ids for:
19673         //  - entityIDs passed in
19674         //  - deep descendant entityIDs for any of those entities that are relations
19675         function utilEntityOrDeepMemberSelector(ids, graph) {
19676             return utilEntitySelector(utilEntityAndDeepMemberIDs(ids, graph));
19677         }
19678
19679
19680         // returns an selector to select entity ids for:
19681         //  - entityIDs passed in
19682         //  - deep descendant entityIDs for any of those entities that are relations
19683         function utilEntityAndDeepMemberIDs(ids, graph) {
19684             var seen = new Set();
19685             ids.forEach(collectDeepDescendants);
19686             return Array.from(seen);
19687
19688             function collectDeepDescendants(id) {
19689                 if (seen.has(id)) { return; }
19690                 seen.add(id);
19691
19692                 var entity = graph.hasEntity(id);
19693                 if (!entity || entity.type !== 'relation') { return; }
19694
19695                 entity.members
19696                     .map(function(member) { return member.id; })
19697                     .forEach(collectDeepDescendants);   // recurse
19698             }
19699         }
19700
19701         // returns an selector to select entity ids for:
19702         //  - deep descendant entityIDs for any of those entities that are relations
19703         function utilDeepMemberSelector(ids, graph, skipMultipolgonMembers) {
19704             var idsSet = new Set(ids);
19705             var seen = new Set();
19706             var returners = new Set();
19707             ids.forEach(collectDeepDescendants);
19708             return utilEntitySelector(Array.from(returners));
19709
19710             function collectDeepDescendants(id) {
19711                 if (seen.has(id)) { return; }
19712                 seen.add(id);
19713
19714                 if (!idsSet.has(id)) {
19715                     returners.add(id);
19716                 }
19717
19718                 var entity = graph.hasEntity(id);
19719                 if (!entity || entity.type !== 'relation') { return; }
19720                 if (skipMultipolgonMembers && entity.isMultipolygon()) { return; }
19721                 entity.members
19722                     .map(function(member) { return member.id; })
19723                     .forEach(collectDeepDescendants);   // recurse
19724             }
19725         }
19726
19727
19728         // Adds or removes highlight styling for the specified entities
19729         function utilHighlightEntities(ids, highlighted, context) {
19730             context.surface()
19731                 .selectAll(utilEntityOrDeepMemberSelector(ids, context.graph()))
19732                 .classed('highlighted', highlighted);
19733         }
19734
19735
19736         // returns an Array that is the union of:
19737         //  - nodes for any nodeIDs passed in
19738         //  - child nodes of any wayIDs passed in
19739         //  - descendant member and child nodes of relationIDs passed in
19740         function utilGetAllNodes(ids, graph) {
19741             var seen = new Set();
19742             var nodes = new Set();
19743
19744             ids.forEach(collectNodes);
19745             return Array.from(nodes);
19746
19747             function collectNodes(id) {
19748                 if (seen.has(id)) { return; }
19749                 seen.add(id);
19750
19751                 var entity = graph.hasEntity(id);
19752                 if (!entity) { return; }
19753
19754                 if (entity.type === 'node') {
19755                     nodes.add(entity);
19756                 } else if (entity.type === 'way') {
19757                     entity.nodes.forEach(collectNodes);
19758                 } else {
19759                     entity.members
19760                         .map(function(member) { return member.id; })
19761                         .forEach(collectNodes);   // recurse
19762                 }
19763             }
19764         }
19765
19766
19767         function utilDisplayName(entity) {
19768             var localizedNameKey = 'name:' + _mainLocalizer.languageCode().toLowerCase();
19769             var name = entity.tags[localizedNameKey] || entity.tags.name || '';
19770             var network = entity.tags.cycle_network || entity.tags.network;
19771
19772             if (!name && entity.tags.ref) {
19773                 name = entity.tags.ref;
19774                 if (network) {
19775                     name = network + ' ' + name;
19776                 }
19777             }
19778
19779             return name;
19780         }
19781
19782
19783         function utilDisplayNameForPath(entity) {
19784             var name = utilDisplayName(entity);
19785             var isFirefox = utilDetect().browser.toLowerCase().indexOf('firefox') > -1;
19786
19787             if (!isFirefox && name && rtlRegex.test(name)) {
19788                 name = fixRTLTextForSvg(name);
19789             }
19790
19791             return name;
19792         }
19793
19794
19795         function utilDisplayType(id) {
19796             return {
19797                 n: _t('inspector.node'),
19798                 w: _t('inspector.way'),
19799                 r: _t('inspector.relation')
19800             }[id.charAt(0)];
19801         }
19802
19803
19804         function utilDisplayLabel(entity, graph) {
19805             var displayName = utilDisplayName(entity);
19806             if (displayName) {
19807                 // use the display name if there is one
19808                 return displayName;
19809             }
19810             var preset = _mainPresetIndex.match(entity, graph);
19811             if (preset && preset.name()) {
19812                 // use the preset name if there is a match
19813                 return preset.name();
19814             }
19815             // fallback to the display type (node/way/relation)
19816             return utilDisplayType(entity.id);
19817         }
19818
19819
19820         function utilEntityRoot(entityType) {
19821             return {
19822                 node: 'n',
19823                 way: 'w',
19824                 relation: 'r'
19825             }[entityType];
19826         }
19827
19828
19829         // Returns a single object containing the tags of all the given entities.
19830         // Example:
19831         // {
19832         //   highway: 'service',
19833         //   service: 'parking_aisle'
19834         // }
19835         //           +
19836         // {
19837         //   highway: 'service',
19838         //   service: 'driveway',
19839         //   width: '3'
19840         // }
19841         //           =
19842         // {
19843         //   highway: 'service',
19844         //   service: [ 'driveway', 'parking_aisle' ],
19845         //   width: [ '3', undefined ]
19846         // }
19847         function utilCombinedTags(entityIDs, graph) {
19848
19849             var tags = {};
19850             var tagCounts = {};
19851             var allKeys = new Set();
19852
19853             var entities = entityIDs.map(function(entityID) {
19854                 return graph.hasEntity(entityID);
19855             }).filter(Boolean);
19856
19857             // gather the aggregate keys
19858             entities.forEach(function(entity) {
19859                 var keys = Object.keys(entity.tags).filter(Boolean);
19860                 keys.forEach(function(key) {
19861                     allKeys.add(key);
19862                 });
19863             });
19864
19865             entities.forEach(function(entity) {
19866
19867                 allKeys.forEach(function(key) {
19868
19869                     var value = entity.tags[key]; // purposely allow `undefined`
19870
19871                     if (!tags.hasOwnProperty(key)) {
19872                         // first value, set as raw
19873                         tags[key] = value;
19874                     } else {
19875                         if (!Array.isArray(tags[key])) {
19876                             if (tags[key] !== value) {
19877                                 // first alternate value, replace single value with array
19878                                 tags[key] = [tags[key], value];
19879                             }
19880                         } else { // type is array
19881                             if (tags[key].indexOf(value) === -1) {
19882                                 // subsequent alternate value, add to array
19883                                 tags[key].push(value);
19884                             }
19885                         }
19886                     }
19887
19888                     var tagHash = key + '=' + value;
19889                     if (!tagCounts[tagHash]) { tagCounts[tagHash] = 0; }
19890                     tagCounts[tagHash] += 1;
19891                 });
19892             });
19893
19894             for (var key in tags) {
19895                 if (!Array.isArray(tags[key])) { continue; }
19896
19897                 // sort values by frequency then alphabetically
19898                 tags[key] = tags[key].sort(function(val1, val2) {
19899                     var key = key; // capture
19900                     var count2 = tagCounts[key + '=' + val2];
19901                     var count1 = tagCounts[key + '=' + val1];
19902                     if (count2 !== count1) {
19903                         return count2 - count1;
19904                     }
19905                     if (val2 && val1) {
19906                         return val1.localeCompare(val2);
19907                     }
19908                     return val1 ? 1 : -1;
19909                 });
19910             }
19911
19912             return tags;
19913         }
19914
19915
19916         function utilStringQs(str) {
19917             var i = 0;  // advance past any leading '?' or '#' characters
19918             while (i < str.length && (str[i] === '?' || str[i] === '#')) { i++; }
19919             str = str.slice(i);
19920
19921             return str.split('&').reduce(function(obj, pair){
19922                 var parts = pair.split('=');
19923                 if (parts.length === 2) {
19924                     obj[parts[0]] = (null === parts[1]) ? '' : decodeURIComponent(parts[1]);
19925                 }
19926                 return obj;
19927             }, {});
19928         }
19929
19930
19931         function utilQsString(obj, noencode) {
19932             // encode everything except special characters used in certain hash parameters:
19933             // "/" in map states, ":", ",", {" and "}" in background
19934             function softEncode(s) {
19935                 return encodeURIComponent(s).replace(/(%2F|%3A|%2C|%7B|%7D)/g, decodeURIComponent);
19936             }
19937
19938             return Object.keys(obj).sort().map(function(key) {
19939                 return encodeURIComponent(key) + '=' + (
19940                     noencode ? softEncode(obj[key]) : encodeURIComponent(obj[key]));
19941             }).join('&');
19942         }
19943
19944
19945         function utilPrefixDOMProperty(property) {
19946             var prefixes = ['webkit', 'ms', 'moz', 'o'];
19947             var i = -1;
19948             var n = prefixes.length;
19949             var s = document.body;
19950
19951             if (property in s)
19952                 { return property; }
19953
19954             property = property.substr(0, 1).toUpperCase() + property.substr(1);
19955
19956             while (++i < n) {
19957                 if (prefixes[i] + property in s) {
19958                     return prefixes[i] + property;
19959                 }
19960             }
19961
19962             return false;
19963         }
19964
19965
19966         function utilPrefixCSSProperty(property) {
19967             var prefixes = ['webkit', 'ms', 'Moz', 'O'];
19968             var i = -1;
19969             var n = prefixes.length;
19970             var s = document.body.style;
19971
19972             if (property.toLowerCase() in s) {
19973                 return property.toLowerCase();
19974             }
19975
19976             while (++i < n) {
19977                 if (prefixes[i] + property in s) {
19978                     return '-' + prefixes[i].toLowerCase() + property.replace(/([A-Z])/g, '-$1').toLowerCase();
19979                 }
19980             }
19981
19982             return false;
19983         }
19984
19985
19986         var transformProperty;
19987         function utilSetTransform(el, x, y, scale) {
19988             var prop = transformProperty = transformProperty || utilPrefixCSSProperty('Transform');
19989             var translate = utilDetect().opera ? 'translate('   + x + 'px,' + y + 'px)'
19990                 : 'translate3d(' + x + 'px,' + y + 'px,0)';
19991             return el.style(prop, translate + (scale ? ' scale(' + scale + ')' : ''));
19992         }
19993
19994
19995         // Calculates Levenshtein distance between two strings
19996         // see:  https://en.wikipedia.org/wiki/Levenshtein_distance
19997         // first converts the strings to lowercase and replaces diacritic marks with ascii equivalents.
19998         function utilEditDistance(a, b) {
19999             a = remove$1(a.toLowerCase());
20000             b = remove$1(b.toLowerCase());
20001             if (a.length === 0) { return b.length; }
20002             if (b.length === 0) { return a.length; }
20003             var matrix = [];
20004             for (var i = 0; i <= b.length; i++) { matrix[i] = [i]; }
20005             for (var j = 0; j <= a.length; j++) { matrix[0][j] = j; }
20006             for (i = 1; i <= b.length; i++) {
20007                 for (j = 1; j <= a.length; j++) {
20008                     if (b.charAt(i-1) === a.charAt(j-1)) {
20009                         matrix[i][j] = matrix[i-1][j-1];
20010                     } else {
20011                         matrix[i][j] = Math.min(matrix[i-1][j-1] + 1, // substitution
20012                             Math.min(matrix[i][j-1] + 1, // insertion
20013                             matrix[i-1][j] + 1)); // deletion
20014                     }
20015                 }
20016             }
20017             return matrix[b.length][a.length];
20018         }
20019
20020
20021         // a d3.mouse-alike which
20022         // 1. Only works on HTML elements, not SVG
20023         // 2. Does not cause style recalculation
20024         function utilFastMouse(container) {
20025             var rect = container.getBoundingClientRect();
20026             var rectLeft = rect.left;
20027             var rectTop = rect.top;
20028             var clientLeft = +container.clientLeft;
20029             var clientTop = +container.clientTop;
20030             return function(e) {
20031                 return [
20032                     e.clientX - rectLeft - clientLeft,
20033                     e.clientY - rectTop - clientTop];
20034             };
20035         }
20036
20037
20038         function utilAsyncMap(inputs, func, callback) {
20039             var remaining = inputs.length;
20040             var results = [];
20041             var errors = [];
20042
20043             inputs.forEach(function(d, i) {
20044                 func(d, function done(err, data) {
20045                     errors[i] = err;
20046                     results[i] = data;
20047                     remaining--;
20048                     if (!remaining) { callback(errors, results); }
20049                 });
20050             });
20051         }
20052
20053
20054         // wraps an index to an interval [0..length-1]
20055         function utilWrap(index, length) {
20056             if (index < 0) {
20057                 index += Math.ceil(-index/length)*length;
20058             }
20059             return index % length;
20060         }
20061
20062
20063         /**
20064          * a replacement for functor
20065          *
20066          * @param {*} value any value
20067          * @returns {Function} a function that returns that value or the value if it's a function
20068          */
20069         function utilFunctor(value) {
20070             if (typeof value === 'function') { return value; }
20071             return function() {
20072                 return value;
20073             };
20074         }
20075
20076
20077         function utilNoAuto(selection) {
20078             var isText = (selection.size() && selection.node().tagName.toLowerCase() === 'textarea');
20079
20080             return selection
20081                 // assign 'new-password' even for non-password fields to prevent browsers (Chrome) ignoring 'off'
20082                 .attr('autocomplete', 'new-password')
20083                 .attr('autocorrect', 'off')
20084                 .attr('autocapitalize', 'off')
20085                 .attr('spellcheck', isText ? 'true' : 'false');
20086         }
20087
20088
20089         // https://stackoverflow.com/questions/194846/is-there-any-kind-of-hash-code-function-in-javascript
20090         // https://werxltd.com/wp/2010/05/13/javascript-implementation-of-javas-string-hashcode-method/
20091         function utilHashcode(str) {
20092             var hash = 0;
20093             if (str.length === 0) {
20094                 return hash;
20095             }
20096             for (var i = 0; i < str.length; i++) {
20097                 var char = str.charCodeAt(i);
20098                 hash = ((hash << 5) - hash) + char;
20099                 hash = hash & hash; // Convert to 32bit integer
20100             }
20101             return hash;
20102         }
20103
20104         // Returns version of `str` with all runs of special characters replaced by `_`;
20105         // suitable for HTML ids, classes, selectors, etc.
20106         function utilSafeClassName(str) {
20107             return str.toLowerCase().replace(/[^a-z0-9]+/g, '_');
20108         }
20109
20110         // Returns string based on `val` that is highly unlikely to collide with an id
20111         // used previously or that's present elsewhere in the document. Useful for preventing
20112         // browser-provided autofills or when embedding iD on pages with unknown elements.
20113         function utilUniqueDomId(val) {
20114             return 'ideditor-' + utilSafeClassName(val.toString()) + '-' + new Date().getTime().toString();
20115         }
20116
20117         // Returns the length of `str` in unicode characters. This can be less than
20118         // `String.length()` since a single unicode character can be composed of multiple
20119         // JavaScript UTF-16 code units.
20120         function utilUnicodeCharsCount(str) {
20121             // Native ES2015 implementations of `Array.from` split strings into unicode characters
20122             return Array.from(str).length;
20123         }
20124
20125         // Returns a new string representing `str` cut from its start to `limit` length
20126         // in unicode characters. Note that this runs the risk of splitting graphemes.
20127         function utilUnicodeCharsTruncated(str, limit) {
20128             return Array.from(str).slice(0, limit).join('');
20129         }
20130
20131         function osmEntity(attrs) {
20132             // For prototypal inheritance.
20133             if (this instanceof osmEntity) { return; }
20134
20135             // Create the appropriate subtype.
20136             if (attrs && attrs.type) {
20137                 return osmEntity[attrs.type].apply(this, arguments);
20138             } else if (attrs && attrs.id) {
20139                 return osmEntity[osmEntity.id.type(attrs.id)].apply(this, arguments);
20140             }
20141
20142             // Initialize a generic Entity (used only in tests).
20143             return (new osmEntity()).initialize(arguments);
20144         }
20145
20146
20147         osmEntity.id = function(type) {
20148             return osmEntity.id.fromOSM(type, osmEntity.id.next[type]--);
20149         };
20150
20151
20152         osmEntity.id.next = {
20153             changeset: -1, node: -1, way: -1, relation: -1
20154         };
20155
20156
20157         osmEntity.id.fromOSM = function(type, id) {
20158             return type[0] + id;
20159         };
20160
20161
20162         osmEntity.id.toOSM = function(id) {
20163             return id.slice(1);
20164         };
20165
20166
20167         osmEntity.id.type = function(id) {
20168             return { 'c': 'changeset', 'n': 'node', 'w': 'way', 'r': 'relation' }[id[0]];
20169         };
20170
20171
20172         // A function suitable for use as the second argument to d3.selection#data().
20173         osmEntity.key = function(entity) {
20174             return entity.id + 'v' + (entity.v || 0);
20175         };
20176
20177         var _deprecatedTagValuesByKey;
20178
20179         osmEntity.deprecatedTagValuesByKey = function(dataDeprecated) {
20180             if (!_deprecatedTagValuesByKey) {
20181                 _deprecatedTagValuesByKey = {};
20182                 dataDeprecated.forEach(function(d) {
20183                     var oldKeys = Object.keys(d.old);
20184                     if (oldKeys.length === 1) {
20185                         var oldKey = oldKeys[0];
20186                         var oldValue = d.old[oldKey];
20187                         if (oldValue !== '*') {
20188                             if (!_deprecatedTagValuesByKey[oldKey]) {
20189                                 _deprecatedTagValuesByKey[oldKey] = [oldValue];
20190                             } else {
20191                                 _deprecatedTagValuesByKey[oldKey].push(oldValue);
20192                             }
20193                         }
20194                     }
20195                 });
20196             }
20197             return _deprecatedTagValuesByKey;
20198         };
20199
20200
20201         osmEntity.prototype = {
20202
20203             tags: {},
20204
20205
20206             initialize: function(sources) {
20207                 for (var i = 0; i < sources.length; ++i) {
20208                     var source = sources[i];
20209                     for (var prop in source) {
20210                         if (Object.prototype.hasOwnProperty.call(source, prop)) {
20211                             if (source[prop] === undefined) {
20212                                 delete this[prop];
20213                             } else {
20214                                 this[prop] = source[prop];
20215                             }
20216                         }
20217                     }
20218                 }
20219
20220                 if (!this.id && this.type) {
20221                     this.id = osmEntity.id(this.type);
20222                 }
20223                 if (!this.hasOwnProperty('visible')) {
20224                     this.visible = true;
20225                 }
20226
20227                 return this;
20228             },
20229
20230
20231             copy: function(resolver, copies) {
20232                 if (copies[this.id])
20233                     { return copies[this.id]; }
20234
20235                 var copy = osmEntity(this, { id: undefined, user: undefined, version: undefined });
20236                 copies[this.id] = copy;
20237
20238                 return copy;
20239             },
20240
20241
20242             osmId: function() {
20243                 return osmEntity.id.toOSM(this.id);
20244             },
20245
20246
20247             isNew: function() {
20248                 return this.osmId() < 0;
20249             },
20250
20251
20252             update: function(attrs) {
20253                 return osmEntity(this, attrs, { v: 1 + (this.v || 0) });
20254             },
20255
20256
20257             mergeTags: function(tags) {
20258                 var merged = Object.assign({}, this.tags);   // shallow copy
20259                 var changed = false;
20260                 for (var k in tags) {
20261                     var t1 = merged[k];
20262                     var t2 = tags[k];
20263                     if (!t1) {
20264                         changed = true;
20265                         merged[k] = t2;
20266                     } else if (t1 !== t2) {
20267                         changed = true;
20268                         merged[k] = utilUnicodeCharsTruncated(
20269                             utilArrayUnion(t1.split(/;\s*/), t2.split(/;\s*/)).join(';'),
20270                             255 // avoid exceeding character limit; see also services/osm.js -> maxCharsForTagValue()
20271                         );
20272                     }
20273                 }
20274                 return changed ? this.update({ tags: merged }) : this;
20275             },
20276
20277
20278             intersects: function(extent, resolver) {
20279                 return this.extent(resolver).intersects(extent);
20280             },
20281
20282
20283             hasNonGeometryTags: function() {
20284                 return Object.keys(this.tags).some(function(k) { return k !== 'area'; });
20285             },
20286
20287             hasParentRelations: function(resolver) {
20288                 return resolver.parentRelations(this).length > 0;
20289             },
20290
20291             hasInterestingTags: function() {
20292                 return Object.keys(this.tags).some(osmIsInterestingTag);
20293             },
20294
20295             hasWikidata: function() {
20296                 return !!this.tags.wikidata || !!this.tags['brand:wikidata'];
20297             },
20298
20299             isHighwayIntersection: function() {
20300                 return false;
20301             },
20302
20303             isDegenerate: function() {
20304                 return true;
20305             },
20306
20307             deprecatedTags: function(dataDeprecated) {
20308                 var tags = this.tags;
20309
20310                 // if there are no tags, none can be deprecated
20311                 if (Object.keys(tags).length === 0) { return []; }
20312
20313                 var deprecated = [];
20314                 dataDeprecated.forEach(function(d) {
20315                     var oldKeys = Object.keys(d.old);
20316                     var matchesDeprecatedTags = oldKeys.every(function(oldKey) {
20317                         if (!tags[oldKey]) { return false; }
20318                         if (d.old[oldKey] === '*') { return true; }
20319
20320                         var vals = tags[oldKey].split(';').filter(Boolean);
20321                         if (vals.length === 0) {
20322                             return false;
20323                         } else if (vals.length > 1) {
20324                             return vals.indexOf(d.old[oldKey]) !== -1;
20325                         } else {
20326                             if (tags[oldKey] === d.old[oldKey]) {
20327                                 if (d.replace && d.old[oldKey] === d.replace[oldKey]) {
20328                                     var replaceKeys = Object.keys(d.replace);
20329                                     return !replaceKeys.every(function(replaceKey) {
20330                                         return tags[replaceKey] === d.replace[replaceKey];
20331                                     });
20332                                 } else {
20333                                     return true;
20334                                 }
20335                             }
20336                         }
20337                         return false;
20338                     });
20339                     if (matchesDeprecatedTags) {
20340                         deprecated.push(d);
20341                     }
20342                 });
20343
20344                 return deprecated;
20345             }
20346         };
20347
20348         function osmLanes(entity) {
20349             if (entity.type !== 'way') { return null; }
20350             if (!entity.tags.highway) { return null; }
20351
20352             var tags = entity.tags;
20353             var isOneWay = entity.isOneWay();
20354             var laneCount = getLaneCount(tags, isOneWay);
20355             var maxspeed = parseMaxspeed(tags);
20356
20357             var laneDirections = parseLaneDirections(tags, isOneWay, laneCount);
20358             var forward = laneDirections.forward;
20359             var backward = laneDirections.backward;
20360             var bothways = laneDirections.bothways;
20361
20362             // parse the piped string 'x|y|z' format
20363             var turnLanes = {};
20364             turnLanes.unspecified = parseTurnLanes(tags['turn:lanes']);
20365             turnLanes.forward = parseTurnLanes(tags['turn:lanes:forward']);
20366             turnLanes.backward = parseTurnLanes(tags['turn:lanes:backward']);
20367
20368             var maxspeedLanes = {};
20369             maxspeedLanes.unspecified = parseMaxspeedLanes(tags['maxspeed:lanes'], maxspeed);
20370             maxspeedLanes.forward = parseMaxspeedLanes(tags['maxspeed:lanes:forward'], maxspeed);
20371             maxspeedLanes.backward = parseMaxspeedLanes(tags['maxspeed:lanes:backward'], maxspeed);
20372
20373             var psvLanes = {};
20374             psvLanes.unspecified = parseMiscLanes(tags['psv:lanes']);
20375             psvLanes.forward = parseMiscLanes(tags['psv:lanes:forward']);
20376             psvLanes.backward = parseMiscLanes(tags['psv:lanes:backward']);
20377
20378             var busLanes = {};
20379             busLanes.unspecified = parseMiscLanes(tags['bus:lanes']);
20380             busLanes.forward = parseMiscLanes(tags['bus:lanes:forward']);
20381             busLanes.backward = parseMiscLanes(tags['bus:lanes:backward']);
20382
20383             var taxiLanes = {};
20384             taxiLanes.unspecified = parseMiscLanes(tags['taxi:lanes']);
20385             taxiLanes.forward = parseMiscLanes(tags['taxi:lanes:forward']);
20386             taxiLanes.backward = parseMiscLanes(tags['taxi:lanes:backward']);
20387
20388             var hovLanes = {};
20389             hovLanes.unspecified = parseMiscLanes(tags['hov:lanes']);
20390             hovLanes.forward = parseMiscLanes(tags['hov:lanes:forward']);
20391             hovLanes.backward = parseMiscLanes(tags['hov:lanes:backward']);
20392
20393             var hgvLanes = {};
20394             hgvLanes.unspecified = parseMiscLanes(tags['hgv:lanes']);
20395             hgvLanes.forward = parseMiscLanes(tags['hgv:lanes:forward']);
20396             hgvLanes.backward = parseMiscLanes(tags['hgv:lanes:backward']);
20397
20398             var bicyclewayLanes = {};
20399             bicyclewayLanes.unspecified = parseBicycleWay(tags['bicycleway:lanes']);
20400             bicyclewayLanes.forward = parseBicycleWay(tags['bicycleway:lanes:forward']);
20401             bicyclewayLanes.backward = parseBicycleWay(tags['bicycleway:lanes:backward']);
20402
20403             var lanesObj = {
20404                 forward: [],
20405                 backward: [],
20406                 unspecified: []
20407             };
20408
20409             // map forward/backward/unspecified of each lane type to lanesObj
20410             mapToLanesObj(lanesObj, turnLanes, 'turnLane');
20411             mapToLanesObj(lanesObj, maxspeedLanes, 'maxspeed');
20412             mapToLanesObj(lanesObj, psvLanes, 'psv');
20413             mapToLanesObj(lanesObj, busLanes, 'bus');
20414             mapToLanesObj(lanesObj, taxiLanes, 'taxi');
20415             mapToLanesObj(lanesObj, hovLanes, 'hov');
20416             mapToLanesObj(lanesObj, hgvLanes, 'hgv');
20417             mapToLanesObj(lanesObj, bicyclewayLanes, 'bicycleway');
20418
20419             return {
20420                 metadata: {
20421                     count: laneCount,
20422                     oneway: isOneWay,
20423                     forward: forward,
20424                     backward: backward,
20425                     bothways: bothways,
20426                     turnLanes: turnLanes,
20427                     maxspeed: maxspeed,
20428                     maxspeedLanes: maxspeedLanes,
20429                     psvLanes: psvLanes,
20430                     busLanes: busLanes,
20431                     taxiLanes: taxiLanes,
20432                     hovLanes: hovLanes,
20433                     hgvLanes: hgvLanes,
20434                     bicyclewayLanes: bicyclewayLanes
20435                 },
20436                 lanes: lanesObj
20437             };
20438         }
20439
20440
20441         function getLaneCount(tags, isOneWay) {
20442             var count;
20443             if (tags.lanes) {
20444                 count = parseInt(tags.lanes, 10);
20445                 if (count > 0) {
20446                     return count;
20447                 }
20448             }
20449
20450
20451             switch (tags.highway) {
20452                 case 'trunk':
20453                 case 'motorway':
20454                     count = isOneWay ? 2 : 4;
20455                     break;
20456                 default:
20457                     count = isOneWay ? 1 : 2;
20458                     break;
20459             }
20460
20461             return count;
20462         }
20463
20464
20465         function parseMaxspeed(tags) {
20466             var maxspeed = tags.maxspeed;
20467             if (!maxspeed) { return; }
20468
20469             var maxspeedRegex = /^([0-9][\.0-9]+?)(?:[ ]?(?:km\/h|kmh|kph|mph|knots))?$/;
20470             if (!maxspeedRegex.test(maxspeed)) { return; }
20471
20472             return parseInt(maxspeed, 10);
20473         }
20474
20475
20476         function parseLaneDirections(tags, isOneWay, laneCount) {
20477             var forward = parseInt(tags['lanes:forward'], 10);
20478             var backward = parseInt(tags['lanes:backward'], 10);
20479             var bothways = parseInt(tags['lanes:both_ways'], 10) > 0 ? 1 : 0;
20480
20481             if (parseInt(tags.oneway, 10) === -1) {
20482                 forward = 0;
20483                 bothways = 0;
20484                 backward = laneCount;
20485             }
20486             else if (isOneWay) {
20487                 forward = laneCount;
20488                 bothways = 0;
20489                 backward = 0;
20490             }
20491             else if (isNaN(forward) && isNaN(backward)) {
20492                 backward = Math.floor((laneCount - bothways) / 2);
20493                 forward = laneCount - bothways - backward;
20494             }
20495             else if (isNaN(forward)) {
20496                 if (backward > laneCount - bothways) {
20497                     backward = laneCount - bothways;
20498                 }
20499                 forward = laneCount - bothways - backward;
20500             }
20501             else if (isNaN(backward)) {
20502                 if (forward > laneCount - bothways) {
20503                     forward = laneCount - bothways;
20504                 }
20505                 backward = laneCount - bothways - forward;
20506             }
20507             return {
20508                 forward: forward,
20509                 backward: backward,
20510                 bothways: bothways
20511             };
20512         }
20513
20514
20515         function parseTurnLanes(tag){
20516             if (!tag) { return; }
20517
20518             var validValues = [
20519                 'left', 'slight_left', 'sharp_left', 'through', 'right', 'slight_right',
20520                 'sharp_right', 'reverse', 'merge_to_left', 'merge_to_right', 'none'
20521             ];
20522
20523             return tag.split('|')
20524                 .map(function (s) {
20525                     if (s === '') { s = 'none'; }
20526                     return s.split(';')
20527                         .map(function (d) {
20528                             return validValues.indexOf(d) === -1 ? 'unknown': d;
20529                         });
20530                 });
20531         }
20532
20533
20534         function parseMaxspeedLanes(tag, maxspeed) {
20535             if (!tag) { return; }
20536
20537             return tag.split('|')
20538                 .map(function (s) {
20539                     if (s === 'none') { return s; }
20540                     var m = parseInt(s, 10);
20541                     if (s === '' || m === maxspeed) { return null; }
20542                     return isNaN(m) ? 'unknown': m;
20543                 });
20544         }
20545
20546
20547         function parseMiscLanes(tag) {
20548             if (!tag) { return; }
20549
20550             var validValues = [
20551                 'yes', 'no', 'designated'
20552             ];
20553
20554             return tag.split('|')
20555                 .map(function (s) {
20556                     if (s === '') { s = 'no'; }
20557                     return validValues.indexOf(s) === -1 ? 'unknown': s;
20558                 });
20559         }
20560
20561
20562         function parseBicycleWay(tag) {
20563             if (!tag) { return; }
20564
20565             var validValues = [
20566                 'yes', 'no', 'designated', 'lane'
20567             ];
20568
20569             return tag.split('|')
20570                 .map(function (s) {
20571                     if (s === '') { s = 'no'; }
20572                     return validValues.indexOf(s) === -1 ? 'unknown': s;
20573                 });
20574         }
20575
20576
20577         function mapToLanesObj(lanesObj, data, key) {
20578             if (data.forward) { data.forward.forEach(function(l, i) {
20579                 if (!lanesObj.forward[i]) { lanesObj.forward[i] = {}; }
20580                 lanesObj.forward[i][key] = l;
20581             }); }
20582             if (data.backward) { data.backward.forEach(function(l, i) {
20583                 if (!lanesObj.backward[i]) { lanesObj.backward[i] = {}; }
20584                 lanesObj.backward[i][key] = l;
20585             }); }
20586             if (data.unspecified) { data.unspecified.forEach(function(l, i) {
20587                 if (!lanesObj.unspecified[i]) { lanesObj.unspecified[i] = {}; }
20588                 lanesObj.unspecified[i][key] = l;
20589             }); }
20590         }
20591
20592         function osmWay() {
20593             if (!(this instanceof osmWay)) {
20594                 return (new osmWay()).initialize(arguments);
20595             } else if (arguments.length) {
20596                 this.initialize(arguments);
20597             }
20598         }
20599
20600
20601         osmEntity.way = osmWay;
20602
20603         osmWay.prototype = Object.create(osmEntity.prototype);
20604
20605
20606         Object.assign(osmWay.prototype, {
20607             type: 'way',
20608             nodes: [],
20609
20610
20611             copy: function(resolver, copies) {
20612                 if (copies[this.id]) { return copies[this.id]; }
20613
20614                 var copy = osmEntity.prototype.copy.call(this, resolver, copies);
20615
20616                 var nodes = this.nodes.map(function(id) {
20617                     return resolver.entity(id).copy(resolver, copies).id;
20618                 });
20619
20620                 copy = copy.update({ nodes: nodes });
20621                 copies[this.id] = copy;
20622
20623                 return copy;
20624             },
20625
20626
20627             extent: function(resolver) {
20628                 return resolver.transient(this, 'extent', function() {
20629                     var extent = geoExtent();
20630                     for (var i = 0; i < this.nodes.length; i++) {
20631                         var node = resolver.hasEntity(this.nodes[i]);
20632                         if (node) {
20633                             extent._extend(node.extent());
20634                         }
20635                     }
20636                     return extent;
20637                 });
20638             },
20639
20640
20641             first: function() {
20642                 return this.nodes[0];
20643             },
20644
20645
20646             last: function() {
20647                 return this.nodes[this.nodes.length - 1];
20648             },
20649
20650
20651             contains: function(node) {
20652                 return this.nodes.indexOf(node) >= 0;
20653             },
20654
20655
20656             affix: function(node) {
20657                 if (this.nodes[0] === node) { return 'prefix'; }
20658                 if (this.nodes[this.nodes.length - 1] === node) { return 'suffix'; }
20659             },
20660
20661
20662             layer: function() {
20663                 // explicit layer tag, clamp between -10, 10..
20664                 if (isFinite(this.tags.layer)) {
20665                     return Math.max(-10, Math.min(+(this.tags.layer), 10));
20666                 }
20667
20668                 // implied layer tag..
20669                 if (this.tags.covered === 'yes') { return -1; }
20670                 if (this.tags.location === 'overground') { return 1; }
20671                 if (this.tags.location === 'underground') { return -1; }
20672                 if (this.tags.location === 'underwater') { return -10; }
20673
20674                 if (this.tags.power === 'line') { return 10; }
20675                 if (this.tags.power === 'minor_line') { return 10; }
20676                 if (this.tags.aerialway) { return 10; }
20677                 if (this.tags.bridge) { return 1; }
20678                 if (this.tags.cutting) { return -1; }
20679                 if (this.tags.tunnel) { return -1; }
20680                 if (this.tags.waterway) { return -1; }
20681                 if (this.tags.man_made === 'pipeline') { return -10; }
20682                 if (this.tags.boundary) { return -10; }
20683                 return 0;
20684             },
20685
20686
20687             // the approximate width of the line based on its tags except its `width` tag
20688             impliedLineWidthMeters: function() {
20689                 var averageWidths = {
20690                     highway: { // width is for single lane
20691                         motorway: 5, motorway_link: 5, trunk: 4.5, trunk_link: 4.5,
20692                         primary: 4, secondary: 4, tertiary: 4,
20693                         primary_link: 4, secondary_link: 4, tertiary_link: 4,
20694                         unclassified: 4, road: 4, living_street: 4, bus_guideway: 4, pedestrian: 4,
20695                         residential: 3.5, service: 3.5, track: 3, cycleway: 2.5,
20696                         bridleway: 2, corridor: 2, steps: 2, path: 1.5, footway: 1.5
20697                     },
20698                     railway: { // width includes ties and rail bed, not just track gauge
20699                         rail: 2.5, light_rail: 2.5, tram: 2.5, subway: 2.5,
20700                         monorail: 2.5, funicular: 2.5, disused: 2.5, preserved: 2.5,
20701                         miniature: 1.5, narrow_gauge: 1.5
20702                     },
20703                     waterway: {
20704                         river: 50, canal: 25, stream: 5, tidal_channel: 5, fish_pass: 2.5, drain: 2.5, ditch: 1.5
20705                     }
20706                 };
20707                 for (var key in averageWidths) {
20708                     if (this.tags[key] && averageWidths[key][this.tags[key]]) {
20709                         var width = averageWidths[key][this.tags[key]];
20710                         if (key === 'highway') {
20711                             var laneCount = this.tags.lanes && parseInt(this.tags.lanes, 10);
20712                             if (!laneCount) { laneCount = this.isOneWay() ? 1 : 2; }
20713
20714                             return width * laneCount;
20715                         }
20716                         return width;
20717                     }
20718                 }
20719                 return null;
20720             },
20721
20722
20723             isOneWay: function() {
20724                 // explicit oneway tag..
20725                 var values = {
20726                     'yes': true,
20727                     '1': true,
20728                     '-1': true,
20729                     'reversible': true,
20730                     'alternating': true,
20731                     'no': false,
20732                     '0': false
20733                 };
20734                 if (values[this.tags.oneway] !== undefined) {
20735                     return values[this.tags.oneway];
20736                 }
20737
20738                 // implied oneway tag..
20739                 for (var key in this.tags) {
20740                     if (key in osmOneWayTags && (this.tags[key] in osmOneWayTags[key]))
20741                         { return true; }
20742                 }
20743                 return false;
20744             },
20745
20746             // Some identifier for tag that implies that this way is "sided",
20747             // i.e. the right side is the 'inside' (e.g. the right side of a
20748             // natural=cliff is lower).
20749             sidednessIdentifier: function() {
20750                 for (var key in this.tags) {
20751                     var value = this.tags[key];
20752                     if (key in osmRightSideIsInsideTags && (value in osmRightSideIsInsideTags[key])) {
20753                         if (osmRightSideIsInsideTags[key][value] === true) {
20754                             return key;
20755                         } else {
20756                             // if the map's value is something other than a
20757                             // literal true, we should use it so we can
20758                             // special case some keys (e.g. natural=coastline
20759                             // is handled differently to other naturals).
20760                             return osmRightSideIsInsideTags[key][value];
20761                         }
20762                     }
20763                 }
20764
20765                 return null;
20766             },
20767
20768             isSided: function() {
20769                 if (this.tags.two_sided === 'yes') {
20770                     return false;
20771                 }
20772
20773                 return this.sidednessIdentifier() !== null;
20774             },
20775
20776             lanes: function() {
20777                 return osmLanes(this);
20778             },
20779
20780
20781             isClosed: function() {
20782                 return this.nodes.length > 1 && this.first() === this.last();
20783             },
20784
20785
20786             isConvex: function(resolver) {
20787                 if (!this.isClosed() || this.isDegenerate()) { return null; }
20788
20789                 var nodes = utilArrayUniq(resolver.childNodes(this));
20790                 var coords = nodes.map(function(n) { return n.loc; });
20791                 var curr = 0;
20792                 var prev = 0;
20793
20794                 for (var i = 0; i < coords.length; i++) {
20795                     var o = coords[(i+1) % coords.length];
20796                     var a = coords[i];
20797                     var b = coords[(i+2) % coords.length];
20798                     var res = geoVecCross(a, b, o);
20799
20800                     curr = (res > 0) ? 1 : (res < 0) ? -1 : 0;
20801                     if (curr === 0) {
20802                         continue;
20803                     } else if (prev && curr !== prev) {
20804                         return false;
20805                     }
20806                     prev = curr;
20807                 }
20808                 return true;
20809             },
20810
20811             // returns an object with the tag that implies this is an area, if any
20812             tagSuggestingArea: function() {
20813                 return osmTagSuggestingArea(this.tags);
20814             },
20815
20816             isArea: function() {
20817                 if (this.tags.area === 'yes')
20818                     { return true; }
20819                 if (!this.isClosed() || this.tags.area === 'no')
20820                     { return false; }
20821                 return this.tagSuggestingArea() !== null;
20822             },
20823
20824
20825             isDegenerate: function() {
20826                 return (new Set(this.nodes).size < (this.isArea() ? 3 : 2));
20827             },
20828
20829
20830             areAdjacent: function(n1, n2) {
20831                 for (var i = 0; i < this.nodes.length; i++) {
20832                     if (this.nodes[i] === n1) {
20833                         if (this.nodes[i - 1] === n2) { return true; }
20834                         if (this.nodes[i + 1] === n2) { return true; }
20835                     }
20836                 }
20837                 return false;
20838             },
20839
20840
20841             geometry: function(graph) {
20842                 return graph.transient(this, 'geometry', function() {
20843                     return this.isArea() ? 'area' : 'line';
20844                 });
20845             },
20846
20847
20848             // returns an array of objects representing the segments between the nodes in this way
20849             segments: function(graph) {
20850
20851                 function segmentExtent(graph) {
20852                     var n1 = graph.hasEntity(this.nodes[0]);
20853                     var n2 = graph.hasEntity(this.nodes[1]);
20854                     return n1 && n2 && geoExtent([
20855                         [
20856                             Math.min(n1.loc[0], n2.loc[0]),
20857                             Math.min(n1.loc[1], n2.loc[1])
20858                         ],
20859                         [
20860                             Math.max(n1.loc[0], n2.loc[0]),
20861                             Math.max(n1.loc[1], n2.loc[1])
20862                         ]
20863                     ]);
20864                 }
20865
20866                 return graph.transient(this, 'segments', function() {
20867                     var segments = [];
20868                     for (var i = 0; i < this.nodes.length - 1; i++) {
20869                         segments.push({
20870                             id: this.id + '-' + i,
20871                             wayId: this.id,
20872                             index: i,
20873                             nodes: [this.nodes[i], this.nodes[i + 1]],
20874                             extent: segmentExtent
20875                         });
20876                     }
20877                     return segments;
20878                 });
20879             },
20880
20881
20882             // If this way is not closed, append the beginning node to the end of the nodelist to close it.
20883             close: function() {
20884                 if (this.isClosed() || !this.nodes.length) { return this; }
20885
20886                 var nodes = this.nodes.slice();
20887                 nodes = nodes.filter(noRepeatNodes);
20888                 nodes.push(nodes[0]);
20889                 return this.update({ nodes: nodes });
20890             },
20891
20892
20893             // If this way is closed, remove any connector nodes from the end of the nodelist to unclose it.
20894             unclose: function() {
20895                 if (!this.isClosed()) { return this; }
20896
20897                 var nodes = this.nodes.slice();
20898                 var connector = this.first();
20899                 var i = nodes.length - 1;
20900
20901                 // remove trailing connectors..
20902                 while (i > 0 && nodes.length > 1 && nodes[i] === connector) {
20903                     nodes.splice(i, 1);
20904                     i = nodes.length - 1;
20905                 }
20906
20907                 nodes = nodes.filter(noRepeatNodes);
20908                 return this.update({ nodes: nodes });
20909             },
20910
20911
20912             // Adds a node (id) in front of the node which is currently at position index.
20913             // If index is undefined, the node will be added to the end of the way for linear ways,
20914             //   or just before the final connecting node for circular ways.
20915             // Consecutive duplicates are eliminated including existing ones.
20916             // Circularity is always preserved when adding a node.
20917             addNode: function(id, index) {
20918                 var nodes = this.nodes.slice();
20919                 var isClosed = this.isClosed();
20920                 var max = isClosed ? nodes.length - 1 : nodes.length;
20921
20922                 if (index === undefined) {
20923                     index = max;
20924                 }
20925
20926                 if (index < 0 || index > max) {
20927                     throw new RangeError('index ' + index + ' out of range 0..' + max);
20928                 }
20929
20930                 // If this is a closed way, remove all connector nodes except the first one
20931                 // (there may be duplicates) and adjust index if necessary..
20932                 if (isClosed) {
20933                     var connector = this.first();
20934
20935                     // leading connectors..
20936                     var i = 1;
20937                     while (i < nodes.length && nodes.length > 2 && nodes[i] === connector) {
20938                         nodes.splice(i, 1);
20939                         if (index > i) { index--; }
20940                     }
20941
20942                     // trailing connectors..
20943                     i = nodes.length - 1;
20944                     while (i > 0 && nodes.length > 1 && nodes[i] === connector) {
20945                         nodes.splice(i, 1);
20946                         if (index > i) { index--; }
20947                         i = nodes.length - 1;
20948                     }
20949                 }
20950
20951                 nodes.splice(index, 0, id);
20952                 nodes = nodes.filter(noRepeatNodes);
20953
20954                 // If the way was closed before, append a connector node to keep it closed..
20955                 if (isClosed && (nodes.length === 1 || nodes[0] !== nodes[nodes.length - 1])) {
20956                     nodes.push(nodes[0]);
20957                 }
20958
20959                 return this.update({ nodes: nodes });
20960             },
20961
20962
20963             // Replaces the node which is currently at position index with the given node (id).
20964             // Consecutive duplicates are eliminated including existing ones.
20965             // Circularity is preserved when updating a node.
20966             updateNode: function(id, index) {
20967                 var nodes = this.nodes.slice();
20968                 var isClosed = this.isClosed();
20969                 var max = nodes.length - 1;
20970
20971                 if (index === undefined || index < 0 || index > max) {
20972                     throw new RangeError('index ' + index + ' out of range 0..' + max);
20973                 }
20974
20975                 // If this is a closed way, remove all connector nodes except the first one
20976                 // (there may be duplicates) and adjust index if necessary..
20977                 if (isClosed) {
20978                     var connector = this.first();
20979
20980                     // leading connectors..
20981                     var i = 1;
20982                     while (i < nodes.length && nodes.length > 2 && nodes[i] === connector) {
20983                         nodes.splice(i, 1);
20984                         if (index > i) { index--; }
20985                     }
20986
20987                     // trailing connectors..
20988                     i = nodes.length - 1;
20989                     while (i > 0 && nodes.length > 1 && nodes[i] === connector) {
20990                         nodes.splice(i, 1);
20991                         if (index === i) { index = 0; }  // update leading connector instead
20992                         i = nodes.length - 1;
20993                     }
20994                 }
20995
20996                 nodes.splice(index, 1, id);
20997                 nodes = nodes.filter(noRepeatNodes);
20998
20999                 // If the way was closed before, append a connector node to keep it closed..
21000                 if (isClosed && (nodes.length === 1 || nodes[0] !== nodes[nodes.length - 1])) {
21001                     nodes.push(nodes[0]);
21002                 }
21003
21004                 return this.update({nodes: nodes});
21005             },
21006
21007
21008             // Replaces each occurrence of node id needle with replacement.
21009             // Consecutive duplicates are eliminated including existing ones.
21010             // Circularity is preserved.
21011             replaceNode: function(needleID, replacementID) {
21012                 var nodes = this.nodes.slice();
21013                 var isClosed = this.isClosed();
21014
21015                 for (var i = 0; i < nodes.length; i++) {
21016                     if (nodes[i] === needleID) {
21017                         nodes[i] = replacementID;
21018                     }
21019                 }
21020
21021                 nodes = nodes.filter(noRepeatNodes);
21022
21023                 // If the way was closed before, append a connector node to keep it closed..
21024                 if (isClosed && (nodes.length === 1 || nodes[0] !== nodes[nodes.length - 1])) {
21025                     nodes.push(nodes[0]);
21026                 }
21027
21028                 return this.update({nodes: nodes});
21029             },
21030
21031
21032             // Removes each occurrence of node id.
21033             // Consecutive duplicates are eliminated including existing ones.
21034             // Circularity is preserved.
21035             removeNode: function(id) {
21036                 var nodes = this.nodes.slice();
21037                 var isClosed = this.isClosed();
21038
21039                 nodes = nodes
21040                     .filter(function(node) { return node !== id; })
21041                     .filter(noRepeatNodes);
21042
21043                 // If the way was closed before, append a connector node to keep it closed..
21044                 if (isClosed && (nodes.length === 1 || nodes[0] !== nodes[nodes.length - 1])) {
21045                     nodes.push(nodes[0]);
21046                 }
21047
21048                 return this.update({nodes: nodes});
21049             },
21050
21051
21052             asJXON: function(changeset_id) {
21053                 var r = {
21054                     way: {
21055                         '@id': this.osmId(),
21056                         '@version': this.version || 0,
21057                         nd: this.nodes.map(function(id) {
21058                             return { keyAttributes: { ref: osmEntity.id.toOSM(id) } };
21059                         }, this),
21060                         tag: Object.keys(this.tags).map(function(k) {
21061                             return { keyAttributes: { k: k, v: this.tags[k] } };
21062                         }, this)
21063                     }
21064                 };
21065                 if (changeset_id) {
21066                     r.way['@changeset'] = changeset_id;
21067                 }
21068                 return r;
21069             },
21070
21071
21072             asGeoJSON: function(resolver) {
21073                 return resolver.transient(this, 'GeoJSON', function() {
21074                     var coordinates = resolver.childNodes(this)
21075                         .map(function(n) { return n.loc; });
21076
21077                     if (this.isArea() && this.isClosed()) {
21078                         return {
21079                             type: 'Polygon',
21080                             coordinates: [coordinates]
21081                         };
21082                     } else {
21083                         return {
21084                             type: 'LineString',
21085                             coordinates: coordinates
21086                         };
21087                     }
21088                 });
21089             },
21090
21091
21092             area: function(resolver) {
21093                 return resolver.transient(this, 'area', function() {
21094                     var nodes = resolver.childNodes(this);
21095
21096                     var json = {
21097                         type: 'Polygon',
21098                         coordinates: [ nodes.map(function(n) { return n.loc; }) ]
21099                     };
21100
21101                     if (!this.isClosed() && nodes.length) {
21102                         json.coordinates[0].push(nodes[0].loc);
21103                     }
21104
21105                     var area = d3_geoArea(json);
21106
21107                     // Heuristic for detecting counterclockwise winding order. Assumes
21108                     // that OpenStreetMap polygons are not hemisphere-spanning.
21109                     if (area > 2 * Math.PI) {
21110                         json.coordinates[0] = json.coordinates[0].reverse();
21111                         area = d3_geoArea(json);
21112                     }
21113
21114                     return isNaN(area) ? 0 : area;
21115                 });
21116             }
21117         });
21118
21119
21120         // Filter function to eliminate consecutive duplicates.
21121         function noRepeatNodes(node, i, arr) {
21122             return i === 0 || node !== arr[i - 1];
21123         }
21124
21125         // "Old" multipolyons, previously known as "simple" multipolygons, are as follows:
21126         //
21127         // 1. Relation tagged with `type=multipolygon` and no interesting tags.
21128         // 2. One and only one member with the `outer` role. Must be a way with interesting tags.
21129         // 3. No members without a role.
21130         //
21131         // Old multipolygons are no longer recommended but are still rendered as areas by iD.
21132
21133         function osmOldMultipolygonOuterMemberOfRelation(entity, graph) {
21134             if (entity.type !== 'relation' ||
21135                 !entity.isMultipolygon()
21136                 || Object.keys(entity.tags).filter(osmIsInterestingTag).length > 1) {
21137                 return false;
21138             }
21139
21140             var outerMember;
21141             for (var memberIndex in entity.members) {
21142                 var member = entity.members[memberIndex];
21143                 if (!member.role || member.role === 'outer') {
21144                     if (outerMember) { return false; }
21145                     if (member.type !== 'way') { return false; }
21146                     if (!graph.hasEntity(member.id)) { return false; }
21147
21148                     outerMember = graph.entity(member.id);
21149
21150                     if (Object.keys(outerMember.tags).filter(osmIsInterestingTag).length === 0) {
21151                         return false;
21152                     }
21153                 }
21154             }
21155
21156             return outerMember;
21157         }
21158
21159         // For fixing up rendering of multipolygons with tags on the outer member.
21160         // https://github.com/openstreetmap/iD/issues/613
21161         function osmIsOldMultipolygonOuterMember(entity, graph) {
21162             if (entity.type !== 'way' || Object.keys(entity.tags).filter(osmIsInterestingTag).length === 0)
21163                 { return false; }
21164
21165             var parents = graph.parentRelations(entity);
21166             if (parents.length !== 1)
21167                 { return false; }
21168
21169             var parent = parents[0];
21170             if (!parent.isMultipolygon() || Object.keys(parent.tags).filter(osmIsInterestingTag).length > 1)
21171                 { return false; }
21172
21173             var members = parent.members, member;
21174             for (var i = 0; i < members.length; i++) {
21175                 member = members[i];
21176                 if (member.id === entity.id && member.role && member.role !== 'outer')
21177                     { return false; } // Not outer member
21178                 if (member.id !== entity.id && (!member.role || member.role === 'outer'))
21179                     { return false; } // Not a simple multipolygon
21180             }
21181
21182             return parent;
21183         }
21184
21185
21186         function osmOldMultipolygonOuterMember(entity, graph) {
21187             if (entity.type !== 'way')
21188                 { return false; }
21189
21190             var parents = graph.parentRelations(entity);
21191             if (parents.length !== 1)
21192                 { return false; }
21193
21194             var parent = parents[0];
21195             if (!parent.isMultipolygon() || Object.keys(parent.tags).filter(osmIsInterestingTag).length > 1)
21196                 { return false; }
21197
21198             var members = parent.members, member, outerMember;
21199             for (var i = 0; i < members.length; i++) {
21200                 member = members[i];
21201                 if (!member.role || member.role === 'outer') {
21202                     if (outerMember)
21203                         { return false; } // Not a simple multipolygon
21204                     outerMember = member;
21205                 }
21206             }
21207
21208             if (!outerMember)
21209                 { return false; }
21210
21211             var outerEntity = graph.hasEntity(outerMember.id);
21212             if (!outerEntity || !Object.keys(outerEntity.tags).filter(osmIsInterestingTag).length)
21213                 { return false; }
21214
21215             return outerEntity;
21216         }
21217
21218
21219         // Join `toJoin` array into sequences of connecting ways.
21220
21221         // Segments which share identical start/end nodes will, as much as possible,
21222         // be connected with each other.
21223         //
21224         // The return value is a nested array. Each constituent array contains elements
21225         // of `toJoin` which have been determined to connect.
21226         //
21227         // Each consitituent array also has a `nodes` property whose value is an
21228         // ordered array of member nodes, with appropriate order reversal and
21229         // start/end coordinate de-duplication.
21230         //
21231         // Members of `toJoin` must have, at minimum, `type` and `id` properties.
21232         // Thus either an array of `osmWay`s or a relation member array may be used.
21233         //
21234         // If an member is an `osmWay`, its tags and childnodes may be reversed via
21235         // `actionReverse` in the output.
21236         //
21237         // The returned sequences array also has an `actions` array property, containing
21238         // any reversal actions that should be applied to the graph, should the calling
21239         // code attempt to actually join the given ways.
21240         //
21241         // Incomplete members (those for which `graph.hasEntity(element.id)` returns
21242         // false) and non-way members are ignored.
21243         //
21244         function osmJoinWays(toJoin, graph) {
21245             function resolve(member) {
21246                 return graph.childNodes(graph.entity(member.id));
21247             }
21248
21249             function reverse(item) {
21250                 var action = actionReverse(item.id, { reverseOneway: true });
21251                 sequences.actions.push(action);
21252                 return (item instanceof osmWay) ? action(graph).entity(item.id) : item;
21253             }
21254
21255             // make a copy containing only the items to join
21256             toJoin = toJoin.filter(function(member) {
21257                 return member.type === 'way' && graph.hasEntity(member.id);
21258             });
21259
21260             // Are the things we are joining relation members or `osmWays`?
21261             // If `osmWays`, skip the "prefer a forward path" code below (see #4872)
21262             var i;
21263             var joinAsMembers = true;
21264             for (i = 0; i < toJoin.length; i++) {
21265                 if (toJoin[i] instanceof osmWay) {
21266                     joinAsMembers = false;
21267                     break;
21268                 }
21269             }
21270
21271             var sequences = [];
21272             sequences.actions = [];
21273
21274             while (toJoin.length) {
21275                 // start a new sequence
21276                 var item = toJoin.shift();
21277                 var currWays = [item];
21278                 var currNodes = resolve(item).slice();
21279                 var doneSequence = false;
21280
21281                 // add to it
21282                 while (toJoin.length && !doneSequence) {
21283                     var start = currNodes[0];
21284                     var end = currNodes[currNodes.length - 1];
21285                     var fn = null;
21286                     var nodes = null;
21287
21288                     // Find the next way/member to join.
21289                     for (i = 0; i < toJoin.length; i++) {
21290                         item = toJoin[i];
21291                         nodes = resolve(item);
21292
21293                         // (for member ordering only, not way ordering - see #4872)
21294                         // Strongly prefer to generate a forward path that preserves the order
21295                         // of the members array. For multipolygons and most relations, member
21296                         // order does not matter - but for routes, it does. (see #4589)
21297                         // If we started this sequence backwards (i.e. next member way attaches to
21298                         // the start node and not the end node), reverse the initial way before continuing.
21299                         if (joinAsMembers && currWays.length === 1 && nodes[0] !== end && nodes[nodes.length - 1] !== end &&
21300                             (nodes[nodes.length - 1] === start || nodes[0] === start)
21301                         ) {
21302                             currWays[0] = reverse(currWays[0]);
21303                             currNodes.reverse();
21304                             start = currNodes[0];
21305                             end = currNodes[currNodes.length - 1];
21306                         }
21307
21308                         if (nodes[0] === end) {
21309                             fn = currNodes.push;               // join to end
21310                             nodes = nodes.slice(1);
21311                             break;
21312                         } else if (nodes[nodes.length - 1] === end) {
21313                             fn = currNodes.push;               // join to end
21314                             nodes = nodes.slice(0, -1).reverse();
21315                             item = reverse(item);
21316                             break;
21317                         } else if (nodes[nodes.length - 1] === start) {
21318                             fn = currNodes.unshift;            // join to beginning
21319                             nodes = nodes.slice(0, -1);
21320                             break;
21321                         } else if (nodes[0] === start) {
21322                             fn = currNodes.unshift;            // join to beginning
21323                             nodes = nodes.slice(1).reverse();
21324                             item = reverse(item);
21325                             break;
21326                         } else {
21327                             fn = nodes = null;
21328                         }
21329                     }
21330
21331                     if (!nodes) {     // couldn't find a joinable way/member
21332                         doneSequence = true;
21333                         break;
21334                     }
21335
21336                     fn.apply(currWays, [item]);
21337                     fn.apply(currNodes, nodes);
21338
21339                     toJoin.splice(i, 1);
21340                 }
21341
21342                 currWays.nodes = currNodes;
21343                 sequences.push(currWays);
21344             }
21345
21346             return sequences;
21347         }
21348
21349         function actionAddMember(relationId, member, memberIndex, insertPair) {
21350
21351             return function action(graph) {
21352                 var relation = graph.entity(relationId);
21353
21354                 // There are some special rules for Public Transport v2 routes.
21355                 var isPTv2 = /stop|platform/.test(member.role);
21356
21357                 if ((isNaN(memberIndex) || insertPair) && member.type === 'way' && !isPTv2) {
21358                     // Try to perform sensible inserts based on how the ways join together
21359                     graph = addWayMember(relation, graph);
21360                 } else {
21361                     // see https://wiki.openstreetmap.org/wiki/Public_transport#Service_routes
21362                     // Stops and Platforms for PTv2 should be ordered first.
21363                     // hack: We do not currently have the ability to place them in the exactly correct order.
21364                     if (isPTv2 && isNaN(memberIndex)) {
21365                         memberIndex = 0;
21366                     }
21367
21368                     graph = graph.replace(relation.addMember(member, memberIndex));
21369                 }
21370
21371                 return graph;
21372             };
21373
21374
21375             // Add a way member into the relation "wherever it makes sense".
21376             // In this situation we were not supplied a memberIndex.
21377             function addWayMember(relation, graph) {
21378                 var groups, tempWay, item, i, j, k;
21379
21380                 // remove PTv2 stops and platforms before doing anything.
21381                 var PTv2members = [];
21382                 var members = [];
21383                 for (i = 0; i < relation.members.length; i++) {
21384                     var m = relation.members[i];
21385                     if (/stop|platform/.test(m.role)) {
21386                         PTv2members.push(m);
21387                     } else {
21388                         members.push(m);
21389                     }
21390                 }
21391                 relation = relation.update({ members: members });
21392
21393
21394                 if (insertPair) {
21395                     // We're adding a member that must stay paired with an existing member.
21396                     // (This feature is used by `actionSplit`)
21397                     //
21398                     // This is tricky because the members may exist multiple times in the
21399                     // member list, and with different A-B/B-A ordering and different roles.
21400                     // (e.g. a bus route that loops out and back - #4589).
21401                     //
21402                     // Replace the existing member with a temporary way,
21403                     // so that `osmJoinWays` can treat the pair like a single way.
21404                     tempWay = osmWay({ id: 'wTemp', nodes: insertPair.nodes });
21405                     graph = graph.replace(tempWay);
21406                     var tempMember = { id: tempWay.id, type: 'way', role: member.role };
21407                     var tempRelation = relation.replaceMember({id: insertPair.originalID}, tempMember, true);
21408                     groups = utilArrayGroupBy(tempRelation.members, 'type');
21409                     groups.way = groups.way || [];
21410
21411                 } else {
21412                     // Add the member anywhere, one time. Just push and let `osmJoinWays` decide where to put it.
21413                     groups = utilArrayGroupBy(relation.members, 'type');
21414                     groups.way = groups.way || [];
21415                     groups.way.push(member);
21416                 }
21417
21418                 members = withIndex(groups.way);
21419                 var joined = osmJoinWays(members, graph);
21420
21421                 // `joined` might not contain all of the way members,
21422                 // But will contain only the completed (downloaded) members
21423                 for (i = 0; i < joined.length; i++) {
21424                     var segment = joined[i];
21425                     var nodes = segment.nodes.slice();
21426                     var startIndex = segment[0].index;
21427
21428                     // j = array index in `members` where this segment starts
21429                     for (j = 0; j < members.length; j++) {
21430                         if (members[j].index === startIndex) {
21431                             break;
21432                         }
21433                     }
21434
21435                     // k = each member in segment
21436                     for (k = 0; k < segment.length; k++) {
21437                         item = segment[k];
21438                         var way = graph.entity(item.id);
21439
21440                         // If this is a paired item, generate members in correct order and role
21441                         if (tempWay && item.id === tempWay.id) {
21442                             if (nodes[0].id === insertPair.nodes[0]) {
21443                                 item.pair = [
21444                                     { id: insertPair.originalID, type: 'way', role: item.role },
21445                                     { id: insertPair.insertedID, type: 'way', role: item.role }
21446                                 ];
21447                             } else {
21448                                 item.pair = [
21449                                     { id: insertPair.insertedID, type: 'way', role: item.role },
21450                                     { id: insertPair.originalID, type: 'way', role: item.role }
21451                                 ];
21452                             }
21453                         }
21454
21455                         // reorder `members` if necessary
21456                         if (k > 0) {
21457                             if (j+k >= members.length || item.index !== members[j+k].index) {
21458                                 moveMember(members, item.index, j+k);
21459                             }
21460                         }
21461
21462                         nodes.splice(0, way.nodes.length - 1);
21463                     }
21464                 }
21465
21466                 if (tempWay) {
21467                     graph = graph.remove(tempWay);
21468                 }
21469
21470                 // Final pass: skip dead items, split pairs, remove index properties
21471                 var wayMembers = [];
21472                 for (i = 0; i < members.length; i++) {
21473                     item = members[i];
21474                     if (item.index === -1) { continue; }
21475
21476                     if (item.pair) {
21477                         wayMembers.push(item.pair[0]);
21478                         wayMembers.push(item.pair[1]);
21479                     } else {
21480                         wayMembers.push(utilObjectOmit(item, ['index']));
21481                     }
21482                 }
21483
21484                 // Put stops and platforms first, then nodes, ways, relations
21485                 // This is recommended for Public Transport v2 routes:
21486                 // see https://wiki.openstreetmap.org/wiki/Public_transport#Service_routes
21487                 var newMembers = PTv2members.concat( (groups.node || []), wayMembers, (groups.relation || []) );
21488
21489                 return graph.replace(relation.update({ members: newMembers }));
21490
21491
21492                 // `moveMember()` changes the `members` array in place by splicing
21493                 // the item with `.index = findIndex` to where it belongs,
21494                 // and marking the old position as "dead" with `.index = -1`
21495                 //
21496                 // j=5, k=0                jk
21497                 // segment                 5 4 7 6
21498                 // members       0 1 2 3 4 5 6 7 8 9        keep 5 in j+k
21499                 //
21500                 // j=5, k=1                j k
21501                 // segment                 5 4 7 6
21502                 // members       0 1 2 3 4 5 6 7 8 9        move 4 to j+k
21503                 // members       0 1 2 3 x 5 4 6 7 8 9      moved
21504                 //
21505                 // j=5, k=2                j   k
21506                 // segment                 5 4 7 6
21507                 // members       0 1 2 3 x 5 4 6 7 8 9      move 7 to j+k
21508                 // members       0 1 2 3 x 5 4 7 6 x 8 9    moved
21509                 //
21510                 // j=5, k=3                j     k
21511                 // segment                 5 4 7 6
21512                 // members       0 1 2 3 x 5 4 7 6 x 8 9    keep 6 in j+k
21513                 //
21514                 function moveMember(arr, findIndex, toIndex) {
21515                     for (var i = 0; i < arr.length; i++) {
21516                         if (arr[i].index === findIndex) {
21517                             break;
21518                         }
21519                     }
21520
21521                     var item = Object.assign({}, arr[i]);   // shallow copy
21522                     arr[i].index = -1;   // mark as dead
21523                     item.index = toIndex;
21524                     arr.splice(toIndex, 0, item);
21525                 }
21526
21527
21528                 // This is the same as `Relation.indexedMembers`,
21529                 // Except we don't want to index all the members, only the ways
21530                 function withIndex(arr) {
21531                     var result = new Array(arr.length);
21532                     for (var i = 0; i < arr.length; i++) {
21533                         result[i] = Object.assign({}, arr[i]);   // shallow copy
21534                         result[i].index = i;
21535                     }
21536                     return result;
21537                 }
21538             }
21539
21540         }
21541
21542         function actionAddMidpoint(midpoint, node) {
21543             return function(graph) {
21544                 graph = graph.replace(node.move(midpoint.loc));
21545
21546                 var parents = utilArrayIntersection(
21547                     graph.parentWays(graph.entity(midpoint.edge[0])),
21548                     graph.parentWays(graph.entity(midpoint.edge[1]))
21549                 );
21550
21551                 parents.forEach(function(way) {
21552                     for (var i = 0; i < way.nodes.length - 1; i++) {
21553                         if (geoEdgeEqual([way.nodes[i], way.nodes[i + 1]], midpoint.edge)) {
21554                             graph = graph.replace(graph.entity(way.id).addNode(node.id, i + 1));
21555
21556                             // Add only one midpoint on doubled-back segments,
21557                             // turning them into self-intersections.
21558                             return;
21559                         }
21560                     }
21561                 });
21562
21563                 return graph;
21564             };
21565         }
21566
21567         // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/AddNodeToWayAction.as
21568         function actionAddVertex(wayId, nodeId, index) {
21569             return function(graph) {
21570                 return graph.replace(graph.entity(wayId).addNode(nodeId, index));
21571             };
21572         }
21573
21574         function actionChangeMember(relationId, member, memberIndex) {
21575             return function(graph) {
21576                 return graph.replace(graph.entity(relationId).updateMember(member, memberIndex));
21577             };
21578         }
21579
21580         function actionChangePreset(entityID, oldPreset, newPreset, skipFieldDefaults) {
21581             return function action(graph) {
21582                 var entity = graph.entity(entityID);
21583                 var geometry = entity.geometry(graph);
21584                 var tags = entity.tags;
21585
21586                 if (oldPreset) { tags = oldPreset.unsetTags(tags, geometry); }
21587                 if (newPreset) { tags = newPreset.setTags(tags, geometry, skipFieldDefaults); }
21588
21589                 return graph.replace(entity.update({tags: tags}));
21590             };
21591         }
21592
21593         function actionChangeTags(entityId, tags) {
21594             return function(graph) {
21595                 var entity = graph.entity(entityId);
21596                 return graph.replace(entity.update({tags: tags}));
21597             };
21598         }
21599
21600         function osmNode() {
21601             if (!(this instanceof osmNode)) {
21602                 return (new osmNode()).initialize(arguments);
21603             } else if (arguments.length) {
21604                 this.initialize(arguments);
21605             }
21606         }
21607
21608         osmEntity.node = osmNode;
21609
21610         osmNode.prototype = Object.create(osmEntity.prototype);
21611
21612         Object.assign(osmNode.prototype, {
21613             type: 'node',
21614             loc: [9999, 9999],
21615
21616             extent: function() {
21617                 return new geoExtent(this.loc);
21618             },
21619
21620
21621             geometry: function(graph) {
21622                 return graph.transient(this, 'geometry', function() {
21623                     return graph.isPoi(this) ? 'point' : 'vertex';
21624                 });
21625             },
21626
21627
21628             move: function(loc) {
21629                 return this.update({loc: loc});
21630             },
21631
21632
21633             isDegenerate: function() {
21634                 return !(
21635                     Array.isArray(this.loc) && this.loc.length === 2 &&
21636                     this.loc[0] >= -180 && this.loc[0] <= 180 &&
21637                     this.loc[1] >= -90 && this.loc[1] <= 90
21638                 );
21639             },
21640
21641
21642             // Inspect tags and geometry to determine which direction(s) this node/vertex points
21643             directions: function(resolver, projection) {
21644                 var val;
21645                 var i;
21646
21647                 // which tag to use?
21648                 if (this.isHighwayIntersection(resolver) && (this.tags.stop || '').toLowerCase() === 'all') {
21649                     // all-way stop tag on a highway intersection
21650                     val = 'all';
21651                 } else {
21652                     // generic direction tag
21653                     val = (this.tags.direction || '').toLowerCase();
21654
21655                     // better suffix-style direction tag
21656                     var re = /:direction$/i;
21657                     var keys = Object.keys(this.tags);
21658                     for (i = 0; i < keys.length; i++) {
21659                         if (re.test(keys[i])) {
21660                             val = this.tags[keys[i]].toLowerCase();
21661                             break;
21662                         }
21663                     }
21664                 }
21665
21666                 if (val === '') { return []; }
21667
21668                 var cardinal = {
21669                     north: 0,               n: 0,
21670                     northnortheast: 22,     nne: 22,
21671                     northeast: 45,          ne: 45,
21672                     eastnortheast: 67,      ene: 67,
21673                     east: 90,               e: 90,
21674                     eastsoutheast: 112,     ese: 112,
21675                     southeast: 135,         se: 135,
21676                     southsoutheast: 157,    sse: 157,
21677                     south: 180,             s: 180,
21678                     southsouthwest: 202,    ssw: 202,
21679                     southwest: 225,         sw: 225,
21680                     westsouthwest: 247,     wsw: 247,
21681                     west: 270,              w: 270,
21682                     westnorthwest: 292,     wnw: 292,
21683                     northwest: 315,         nw: 315,
21684                     northnorthwest: 337,    nnw: 337
21685                 };
21686
21687
21688                 var values = val.split(';');
21689                 var results = [];
21690
21691                 values.forEach(function(v) {
21692                     // swap cardinal for numeric directions
21693                     if (cardinal[v] !== undefined) {
21694                         v = cardinal[v];
21695                     }
21696
21697                     // numeric direction - just add to results
21698                     if (v !== '' && !isNaN(+v)) {
21699                         results.push(+v);
21700                         return;
21701                     }
21702
21703                     // string direction - inspect parent ways
21704                     var lookBackward =
21705                         (this.tags['traffic_sign:backward'] || v === 'backward' || v === 'both' || v === 'all');
21706                     var lookForward =
21707                         (this.tags['traffic_sign:forward'] || v === 'forward' || v === 'both' || v === 'all');
21708
21709                     if (!lookForward && !lookBackward) { return; }
21710
21711                     var nodeIds = {};
21712                     resolver.parentWays(this).forEach(function(parent) {
21713                         var nodes = parent.nodes;
21714                         for (i = 0; i < nodes.length; i++) {
21715                             if (nodes[i] === this.id) {  // match current entity
21716                                 if (lookForward && i > 0) {
21717                                     nodeIds[nodes[i - 1]] = true;  // look back to prev node
21718                                 }
21719                                 if (lookBackward && i < nodes.length - 1) {
21720                                     nodeIds[nodes[i + 1]] = true;  // look ahead to next node
21721                                 }
21722                             }
21723                         }
21724                     }, this);
21725
21726                     Object.keys(nodeIds).forEach(function(nodeId) {
21727                         // +90 because geoAngle returns angle from X axis, not Y (north)
21728                         results.push(
21729                             (geoAngle(this, resolver.entity(nodeId), projection) * (180 / Math.PI)) + 90
21730                         );
21731                     }, this);
21732
21733                 }, this);
21734
21735                 return utilArrayUniq(results);
21736             },
21737
21738
21739             isEndpoint: function(resolver) {
21740                 return resolver.transient(this, 'isEndpoint', function() {
21741                     var id = this.id;
21742                     return resolver.parentWays(this).filter(function(parent) {
21743                         return !parent.isClosed() && !!parent.affix(id);
21744                     }).length > 0;
21745                 });
21746             },
21747
21748
21749             isConnected: function(resolver) {
21750                 return resolver.transient(this, 'isConnected', function() {
21751                     var parents = resolver.parentWays(this);
21752
21753                     if (parents.length > 1) {
21754                         // vertex is connected to multiple parent ways
21755                         for (var i in parents) {
21756                             if (parents[i].geometry(resolver) === 'line' &&
21757                                 parents[i].hasInterestingTags()) { return true; }
21758                         }
21759                     } else if (parents.length === 1) {
21760                         var way = parents[0];
21761                         var nodes = way.nodes.slice();
21762                         if (way.isClosed()) { nodes.pop(); }  // ignore connecting node if closed
21763
21764                         // return true if vertex appears multiple times (way is self intersecting)
21765                         return nodes.indexOf(this.id) !== nodes.lastIndexOf(this.id);
21766                     }
21767
21768                     return false;
21769                 });
21770             },
21771
21772
21773             parentIntersectionWays: function(resolver) {
21774                 return resolver.transient(this, 'parentIntersectionWays', function() {
21775                     return resolver.parentWays(this).filter(function(parent) {
21776                         return (parent.tags.highway ||
21777                             parent.tags.waterway ||
21778                             parent.tags.railway ||
21779                             parent.tags.aeroway) &&
21780                             parent.geometry(resolver) === 'line';
21781                     });
21782                 });
21783             },
21784
21785
21786             isIntersection: function(resolver) {
21787                 return this.parentIntersectionWays(resolver).length > 1;
21788             },
21789
21790
21791             isHighwayIntersection: function(resolver) {
21792                 return resolver.transient(this, 'isHighwayIntersection', function() {
21793                     return resolver.parentWays(this).filter(function(parent) {
21794                         return parent.tags.highway && parent.geometry(resolver) === 'line';
21795                     }).length > 1;
21796                 });
21797             },
21798
21799
21800             isOnAddressLine: function(resolver) {
21801                 return resolver.transient(this, 'isOnAddressLine', function() {
21802                     return resolver.parentWays(this).filter(function(parent) {
21803                         return parent.tags.hasOwnProperty('addr:interpolation') &&
21804                             parent.geometry(resolver) === 'line';
21805                     }).length > 0;
21806                 });
21807             },
21808
21809
21810             asJXON: function(changeset_id) {
21811                 var r = {
21812                     node: {
21813                         '@id': this.osmId(),
21814                         '@lon': this.loc[0],
21815                         '@lat': this.loc[1],
21816                         '@version': (this.version || 0),
21817                         tag: Object.keys(this.tags).map(function(k) {
21818                             return { keyAttributes: { k: k, v: this.tags[k] } };
21819                         }, this)
21820                     }
21821                 };
21822                 if (changeset_id) { r.node['@changeset'] = changeset_id; }
21823                 return r;
21824             },
21825
21826
21827             asGeoJSON: function() {
21828                 return {
21829                     type: 'Point',
21830                     coordinates: this.loc
21831                 };
21832             }
21833         });
21834
21835         function actionCircularize(wayId, projection, maxAngle) {
21836             maxAngle = (maxAngle || 20) * Math.PI / 180;
21837
21838
21839             var action = function(graph, t) {
21840                 if (t === null || !isFinite(t)) { t = 1; }
21841                 t = Math.min(Math.max(+t, 0), 1);
21842
21843                 var way = graph.entity(wayId);
21844                 var origNodes = {};
21845
21846                 graph.childNodes(way).forEach(function(node) {
21847                     if (!origNodes[node.id]) { origNodes[node.id] = node; }
21848                 });
21849
21850                 if (!way.isConvex(graph)) {
21851                     graph = action.makeConvex(graph);
21852                 }
21853
21854                 var nodes = utilArrayUniq(graph.childNodes(way));
21855                 var keyNodes = nodes.filter(function(n) { return graph.parentWays(n).length !== 1; });
21856                 var points = nodes.map(function(n) { return projection(n.loc); });
21857                 var keyPoints = keyNodes.map(function(n) { return projection(n.loc); });
21858                 var centroid = (points.length === 2) ? geoVecInterp(points[0], points[1], 0.5) : d3_polygonCentroid(points);
21859                 var radius = d3_median(points, function(p) { return geoVecLength(centroid, p); });
21860                 var sign = d3_polygonArea(points) > 0 ? 1 : -1;
21861                 var ids, i, j, k;
21862
21863                 // we need at least two key nodes for the algorithm to work
21864                 if (!keyNodes.length) {
21865                     keyNodes = [nodes[0]];
21866                     keyPoints = [points[0]];
21867                 }
21868
21869                 if (keyNodes.length === 1) {
21870                     var index = nodes.indexOf(keyNodes[0]);
21871                     var oppositeIndex = Math.floor((index + nodes.length / 2) % nodes.length);
21872
21873                     keyNodes.push(nodes[oppositeIndex]);
21874                     keyPoints.push(points[oppositeIndex]);
21875                 }
21876
21877                 // key points and nodes are those connected to the ways,
21878                 // they are projected onto the circle, in between nodes are moved
21879                 // to constant intervals between key nodes, extra in between nodes are
21880                 // added if necessary.
21881                 for (i = 0; i < keyPoints.length; i++) {
21882                     var nextKeyNodeIndex = (i + 1) % keyNodes.length;
21883                     var startNode = keyNodes[i];
21884                     var endNode = keyNodes[nextKeyNodeIndex];
21885                     var startNodeIndex = nodes.indexOf(startNode);
21886                     var endNodeIndex = nodes.indexOf(endNode);
21887                     var numberNewPoints = -1;
21888                     var indexRange = endNodeIndex - startNodeIndex;
21889                     var nearNodes = {};
21890                     var inBetweenNodes = [];
21891                     var startAngle, endAngle, totalAngle, eachAngle;
21892                     var angle, loc, node, origNode;
21893
21894                     if (indexRange < 0) {
21895                         indexRange += nodes.length;
21896                     }
21897
21898                     // position this key node
21899                     var distance = geoVecLength(centroid, keyPoints[i]) || 1e-4;
21900                     keyPoints[i] = [
21901                         centroid[0] + (keyPoints[i][0] - centroid[0]) / distance * radius,
21902                         centroid[1] + (keyPoints[i][1] - centroid[1]) / distance * radius
21903                     ];
21904                     loc = projection.invert(keyPoints[i]);
21905                     node = keyNodes[i];
21906                     origNode = origNodes[node.id];
21907                     node = node.move(geoVecInterp(origNode.loc, loc, t));
21908                     graph = graph.replace(node);
21909
21910                     // figure out the between delta angle we want to match to
21911                     startAngle = Math.atan2(keyPoints[i][1] - centroid[1], keyPoints[i][0] - centroid[0]);
21912                     endAngle = Math.atan2(keyPoints[nextKeyNodeIndex][1] - centroid[1], keyPoints[nextKeyNodeIndex][0] - centroid[0]);
21913                     totalAngle = endAngle - startAngle;
21914
21915                     // detects looping around -pi/pi
21916                     if (totalAngle * sign > 0) {
21917                         totalAngle = -sign * (2 * Math.PI - Math.abs(totalAngle));
21918                     }
21919
21920                     do {
21921                         numberNewPoints++;
21922                         eachAngle = totalAngle / (indexRange + numberNewPoints);
21923                     } while (Math.abs(eachAngle) > maxAngle);
21924
21925
21926                     // move existing nodes
21927                     for (j = 1; j < indexRange; j++) {
21928                         angle = startAngle + j * eachAngle;
21929                         loc = projection.invert([
21930                             centroid[0] + Math.cos(angle) * radius,
21931                             centroid[1] + Math.sin(angle) * radius
21932                         ]);
21933
21934                         node = nodes[(j + startNodeIndex) % nodes.length];
21935                         origNode = origNodes[node.id];
21936                         nearNodes[node.id] = angle;
21937
21938                         node = node.move(geoVecInterp(origNode.loc, loc, t));
21939                         graph = graph.replace(node);
21940                     }
21941
21942                     // add new in between nodes if necessary
21943                     for (j = 0; j < numberNewPoints; j++) {
21944                         angle = startAngle + (indexRange + j) * eachAngle;
21945                         loc = projection.invert([
21946                             centroid[0] + Math.cos(angle) * radius,
21947                             centroid[1] + Math.sin(angle) * radius
21948                         ]);
21949
21950                         // choose a nearnode to use as the original
21951                         var min = Infinity;
21952                         for (var nodeId in nearNodes) {
21953                             var nearAngle = nearNodes[nodeId];
21954                             var dist = Math.abs(nearAngle - angle);
21955                             if (dist < min) {
21956                                 dist = min;
21957                                 origNode = origNodes[nodeId];
21958                             }
21959                         }
21960
21961                         node = osmNode({ loc: geoVecInterp(origNode.loc, loc, t) });
21962                         graph = graph.replace(node);
21963
21964                         nodes.splice(endNodeIndex + j, 0, node);
21965                         inBetweenNodes.push(node.id);
21966                     }
21967
21968                     // Check for other ways that share these keyNodes..
21969                     // If keyNodes are adjacent in both ways,
21970                     // we can add inBetweenNodes to that shared way too..
21971                     if (indexRange === 1 && inBetweenNodes.length) {
21972                         var startIndex1 = way.nodes.lastIndexOf(startNode.id);
21973                         var endIndex1 = way.nodes.lastIndexOf(endNode.id);
21974                         var wayDirection1 = (endIndex1 - startIndex1);
21975                         if (wayDirection1 < -1) { wayDirection1 = 1; }
21976
21977                         var parentWays = graph.parentWays(keyNodes[i]);
21978                         for (j = 0; j < parentWays.length; j++) {
21979                             var sharedWay = parentWays[j];
21980                             if (sharedWay === way) { continue; }
21981
21982                             if (sharedWay.areAdjacent(startNode.id, endNode.id)) {
21983                                 var startIndex2 = sharedWay.nodes.lastIndexOf(startNode.id);
21984                                 var endIndex2 = sharedWay.nodes.lastIndexOf(endNode.id);
21985                                 var wayDirection2 = (endIndex2 - startIndex2);
21986                                 var insertAt = endIndex2;
21987                                 if (wayDirection2 < -1) { wayDirection2 = 1; }
21988
21989                                 if (wayDirection1 !== wayDirection2) {
21990                                     inBetweenNodes.reverse();
21991                                     insertAt = startIndex2;
21992                                 }
21993                                 for (k = 0; k < inBetweenNodes.length; k++) {
21994                                     sharedWay = sharedWay.addNode(inBetweenNodes[k], insertAt + k);
21995                                 }
21996                                 graph = graph.replace(sharedWay);
21997                             }
21998                         }
21999                     }
22000
22001                 }
22002
22003                 // update the way to have all the new nodes
22004                 ids = nodes.map(function(n) { return n.id; });
22005                 ids.push(ids[0]);
22006
22007                 way = way.update({nodes: ids});
22008                 graph = graph.replace(way);
22009
22010                 return graph;
22011             };
22012
22013
22014             action.makeConvex = function(graph) {
22015                 var way = graph.entity(wayId);
22016                 var nodes = utilArrayUniq(graph.childNodes(way));
22017                 var points = nodes.map(function(n) { return projection(n.loc); });
22018                 var sign = d3_polygonArea(points) > 0 ? 1 : -1;
22019                 var hull = d3_polygonHull(points);
22020                 var i, j;
22021
22022                 // D3 convex hulls go counterclockwise..
22023                 if (sign === -1) {
22024                     nodes.reverse();
22025                     points.reverse();
22026                 }
22027
22028                 for (i = 0; i < hull.length - 1; i++) {
22029                     var startIndex = points.indexOf(hull[i]);
22030                     var endIndex = points.indexOf(hull[i+1]);
22031                     var indexRange = (endIndex - startIndex);
22032
22033                     if (indexRange < 0) {
22034                         indexRange += nodes.length;
22035                     }
22036
22037                     // move interior nodes to the surface of the convex hull..
22038                     for (j = 1; j < indexRange; j++) {
22039                         var point = geoVecInterp(hull[i], hull[i+1], j / indexRange);
22040                         var node = nodes[(j + startIndex) % nodes.length].move(projection.invert(point));
22041                         graph = graph.replace(node);
22042                     }
22043                 }
22044                 return graph;
22045             };
22046
22047
22048             action.disabled = function(graph) {
22049                 if (!graph.entity(wayId).isClosed()) {
22050                     return 'not_closed';
22051                 }
22052
22053                 //disable when already circular
22054                 var way = graph.entity(wayId);
22055                 var nodes = utilArrayUniq(graph.childNodes(way));
22056                 var points = nodes.map(function(n) { return projection(n.loc); });
22057                 var hull = d3_polygonHull(points);
22058                 var epsilonAngle =  Math.PI / 180;
22059                 if (hull.length !== points.length || hull.length < 3){
22060                     return false;
22061                 }
22062                 var centroid = d3_polygonCentroid(points);
22063                 var radius = geoVecLengthSquare(centroid, points[0]);
22064
22065                 // compare distances between centroid and points
22066                 for (var i = 0; i<hull.length; i++){
22067                     var actualPoint = hull[i];
22068                     var actualDist = geoVecLengthSquare(actualPoint, centroid);
22069                     var diff = Math.abs(actualDist - radius);
22070                     //compare distances with epsilon-error (5%)
22071                     if (diff > 0.05*radius) {
22072                         return false;
22073                     }
22074                 }
22075                 
22076                 //check if central angles are smaller than maxAngle
22077                 for (i = 0; i<hull.length; i++){
22078                     actualPoint = hull[i];
22079                     var nextPoint = hull[(i+1)%hull.length];
22080                     var startAngle = Math.atan2(actualPoint[1] - centroid[1], actualPoint[0] - centroid[0]);
22081                     var endAngle = Math.atan2(nextPoint[1] - centroid[1], nextPoint[0] - centroid[0]);
22082                     var angle = endAngle - startAngle;
22083                     if (angle < 0) {
22084                         angle = -angle;
22085                     }
22086                     if (angle > Math.PI){
22087                         angle = (2*Math.PI - angle);
22088                     }
22089          
22090                     if (angle > maxAngle + epsilonAngle) {
22091                         return false;
22092                     }
22093                 }
22094                 return 'already_circular';
22095             };
22096
22097
22098             action.transitionable = true;
22099
22100
22101             return action;
22102         }
22103
22104         // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/DeleteWayAction.as
22105         function actionDeleteWay(wayID) {
22106
22107             function canDeleteNode(node, graph) {
22108                 // don't delete nodes still attached to ways or relations
22109                 if (graph.parentWays(node).length ||
22110                     graph.parentRelations(node).length) { return false; }
22111
22112                 var geometries = osmNodeGeometriesForTags(node.tags);
22113                 // don't delete if this node can be a standalone point
22114                 if (geometries.point) { return false; }
22115                 // delete if this node only be a vertex
22116                 if (geometries.vertex) { return true; }
22117
22118                 // iD doesn't know if this should be a point or vertex,
22119                 // so only delete if there are no interesting tags
22120                 return !node.hasInterestingTags();
22121             }
22122
22123
22124             var action = function(graph) {
22125                 var way = graph.entity(wayID);
22126
22127                 graph.parentRelations(way).forEach(function(parent) {
22128                     parent = parent.removeMembersWithID(wayID);
22129                     graph = graph.replace(parent);
22130
22131                     if (parent.isDegenerate()) {
22132                         graph = actionDeleteRelation(parent.id)(graph);
22133                     }
22134                 });
22135
22136                 (new Set(way.nodes)).forEach(function(nodeID) {
22137                     graph = graph.replace(way.removeNode(nodeID));
22138
22139                     var node = graph.entity(nodeID);
22140                     if (canDeleteNode(node, graph)) {
22141                         graph = graph.remove(node);
22142                     }
22143                 });
22144
22145                 return graph.remove(way);
22146             };
22147
22148
22149             return action;
22150         }
22151
22152         function actionDeleteMultiple(ids) {
22153             var actions = {
22154                 way: actionDeleteWay,
22155                 node: actionDeleteNode,
22156                 relation: actionDeleteRelation
22157             };
22158
22159
22160             var action = function(graph) {
22161                 ids.forEach(function(id) {
22162                     if (graph.hasEntity(id)) { // It may have been deleted already.
22163                         graph = actions[graph.entity(id).type](id)(graph);
22164                     }
22165                 });
22166
22167                 return graph;
22168             };
22169
22170
22171             return action;
22172         }
22173
22174         // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/DeleteRelationAction.as
22175         function actionDeleteRelation(relationID, allowUntaggedMembers) {
22176
22177             function canDeleteEntity(entity, graph) {
22178                 return !graph.parentWays(entity).length &&
22179                     !graph.parentRelations(entity).length &&
22180                     (!entity.hasInterestingTags() && !allowUntaggedMembers);
22181             }
22182
22183
22184             var action = function(graph) {
22185                 var relation = graph.entity(relationID);
22186
22187                 graph.parentRelations(relation)
22188                     .forEach(function(parent) {
22189                         parent = parent.removeMembersWithID(relationID);
22190                         graph = graph.replace(parent);
22191
22192                         if (parent.isDegenerate()) {
22193                             graph = actionDeleteRelation(parent.id)(graph);
22194                         }
22195                     });
22196
22197                 var memberIDs = utilArrayUniq(relation.members.map(function(m) { return m.id; }));
22198                 memberIDs.forEach(function(memberID) {
22199                     graph = graph.replace(relation.removeMembersWithID(memberID));
22200
22201                     var entity = graph.entity(memberID);
22202                     if (canDeleteEntity(entity, graph)) {
22203                         graph = actionDeleteMultiple([memberID])(graph);
22204                     }
22205                 });
22206
22207                 return graph.remove(relation);
22208             };
22209
22210
22211             return action;
22212         }
22213
22214         // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/DeleteNodeAction.as
22215         function actionDeleteNode(nodeId) {
22216             var action = function(graph) {
22217                 var node = graph.entity(nodeId);
22218
22219                 graph.parentWays(node)
22220                     .forEach(function(parent) {
22221                         parent = parent.removeNode(nodeId);
22222                         graph = graph.replace(parent);
22223
22224                         if (parent.isDegenerate()) {
22225                             graph = actionDeleteWay(parent.id)(graph);
22226                         }
22227                     });
22228
22229                 graph.parentRelations(node)
22230                     .forEach(function(parent) {
22231                         parent = parent.removeMembersWithID(nodeId);
22232                         graph = graph.replace(parent);
22233
22234                         if (parent.isDegenerate()) {
22235                             graph = actionDeleteRelation(parent.id)(graph);
22236                         }
22237                     });
22238
22239                 return graph.remove(node);
22240             };
22241
22242
22243             return action;
22244         }
22245
22246         // Connect the ways at the given nodes.
22247         //
22248         // First choose a node to be the survivor, with preference given
22249         // to an existing (not new) node.
22250         //
22251         // Tags and relation memberships of of non-surviving nodes are merged
22252         // to the survivor.
22253         //
22254         // This is the inverse of `iD.actionDisconnect`.
22255         //
22256         // Reference:
22257         //   https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/MergeNodesAction.as
22258         //   https://github.com/openstreetmap/josm/blob/mirror/src/org/openstreetmap/josm/actions/MergeNodesAction.java
22259         //
22260         function actionConnect(nodeIDs) {
22261             var action = function(graph) {
22262                 var survivor;
22263                 var node;
22264                 var parents;
22265                 var i, j;
22266
22267                 // Choose a survivor node, prefer an existing (not new) node - #4974
22268                 for (i = 0; i < nodeIDs.length; i++) {
22269                     survivor = graph.entity(nodeIDs[i]);
22270                     if (survivor.version) { break; }  // found one
22271                 }
22272
22273                 // Replace all non-surviving nodes with the survivor and merge tags.
22274                 for (i = 0; i < nodeIDs.length; i++) {
22275                     node = graph.entity(nodeIDs[i]);
22276                     if (node.id === survivor.id) { continue; }
22277
22278                     parents = graph.parentWays(node);
22279                     for (j = 0; j < parents.length; j++) {
22280                         graph = graph.replace(parents[j].replaceNode(node.id, survivor.id));
22281                     }
22282
22283                     parents = graph.parentRelations(node);
22284                     for (j = 0; j < parents.length; j++) {
22285                         graph = graph.replace(parents[j].replaceMember(node, survivor));
22286                     }
22287
22288                     survivor = survivor.mergeTags(node.tags);
22289                     graph = actionDeleteNode(node.id)(graph);
22290                 }
22291
22292                 graph = graph.replace(survivor);
22293
22294                 // find and delete any degenerate ways created by connecting adjacent vertices
22295                 parents = graph.parentWays(survivor);
22296                 for (i = 0; i < parents.length; i++) {
22297                     if (parents[i].isDegenerate()) {
22298                         graph = actionDeleteWay(parents[i].id)(graph);
22299                     }
22300                 }
22301
22302                 return graph;
22303             };
22304
22305
22306             action.disabled = function(graph) {
22307                 var seen = {};
22308                 var restrictionIDs = [];
22309                 var survivor;
22310                 var node, way;
22311                 var relations, relation, role;
22312                 var i, j, k;
22313
22314                 // Choose a survivor node, prefer an existing (not new) node - #4974
22315                 for (i = 0; i < nodeIDs.length; i++) {
22316                     survivor = graph.entity(nodeIDs[i]);
22317                     if (survivor.version) { break; }  // found one
22318                 }
22319
22320                 // 1. disable if the nodes being connected have conflicting relation roles
22321                 for (i = 0; i < nodeIDs.length; i++) {
22322                     node = graph.entity(nodeIDs[i]);
22323                     relations = graph.parentRelations(node);
22324
22325                     for (j = 0; j < relations.length; j++) {
22326                         relation = relations[j];
22327                         role = relation.memberById(node.id).role || '';
22328
22329                         // if this node is a via node in a restriction, remember for later
22330                         if (relation.hasFromViaTo()) {
22331                             restrictionIDs.push(relation.id);
22332                         }
22333
22334                         if (seen[relation.id] !== undefined && seen[relation.id] !== role) {
22335                             return 'relation';
22336                         } else {
22337                             seen[relation.id] = role;
22338                         }
22339                     }
22340                 }
22341
22342                 // gather restrictions for parent ways
22343                 for (i = 0; i < nodeIDs.length; i++) {
22344                     node = graph.entity(nodeIDs[i]);
22345
22346                     var parents = graph.parentWays(node);
22347                     for (j = 0; j < parents.length; j++) {
22348                         var parent = parents[j];
22349                         relations = graph.parentRelations(parent);
22350
22351                         for (k = 0; k < relations.length; k++) {
22352                             relation = relations[k];
22353                             if (relation.hasFromViaTo()) {
22354                                 restrictionIDs.push(relation.id);
22355                             }
22356                         }
22357                     }
22358                 }
22359
22360
22361                 // test restrictions
22362                 restrictionIDs = utilArrayUniq(restrictionIDs);
22363                 for (i = 0; i < restrictionIDs.length; i++) {
22364                     relation = graph.entity(restrictionIDs[i]);
22365                     if (!relation.isComplete(graph)) { continue; }
22366
22367                     var memberWays = relation.members
22368                         .filter(function(m) { return m.type === 'way'; })
22369                         .map(function(m) { return graph.entity(m.id); });
22370
22371                     memberWays = utilArrayUniq(memberWays);
22372                     var f = relation.memberByRole('from');
22373                     var t = relation.memberByRole('to');
22374                     var isUturn = (f.id === t.id);
22375
22376                     // 2a. disable if connection would damage a restriction
22377                     // (a key node is a node at the junction of ways)
22378                     var nodes = { from: [], via: [], to: [], keyfrom: [], keyto: [] };
22379                     for (j = 0; j < relation.members.length; j++) {
22380                         collectNodes(relation.members[j], nodes);
22381                     }
22382
22383                     nodes.keyfrom = utilArrayUniq(nodes.keyfrom.filter(hasDuplicates));
22384                     nodes.keyto = utilArrayUniq(nodes.keyto.filter(hasDuplicates));
22385
22386                     var filter = keyNodeFilter(nodes.keyfrom, nodes.keyto);
22387                     nodes.from = nodes.from.filter(filter);
22388                     nodes.via = nodes.via.filter(filter);
22389                     nodes.to = nodes.to.filter(filter);
22390
22391                     var connectFrom = false;
22392                     var connectVia = false;
22393                     var connectTo = false;
22394                     var connectKeyFrom = false;
22395                     var connectKeyTo = false;
22396
22397                     for (j = 0; j < nodeIDs.length; j++) {
22398                         var n = nodeIDs[j];
22399                         if (nodes.from.indexOf(n) !== -1)    { connectFrom = true; }
22400                         if (nodes.via.indexOf(n) !== -1)     { connectVia = true; }
22401                         if (nodes.to.indexOf(n) !== -1)      { connectTo = true; }
22402                         if (nodes.keyfrom.indexOf(n) !== -1) { connectKeyFrom = true; }
22403                         if (nodes.keyto.indexOf(n) !== -1)   { connectKeyTo = true; }
22404                     }
22405                     if (connectFrom && connectTo && !isUturn) { return 'restriction'; }
22406                     if (connectFrom && connectVia) { return 'restriction'; }
22407                     if (connectTo   && connectVia) { return 'restriction'; }
22408
22409                     // connecting to a key node -
22410                     // if both nodes are on a member way (i.e. part of the turn restriction),
22411                     // the connecting node must be adjacent to the key node.
22412                     if (connectKeyFrom || connectKeyTo) {
22413                         if (nodeIDs.length !== 2) { return 'restriction'; }
22414
22415                         var n0 = null;
22416                         var n1 = null;
22417                         for (j = 0; j < memberWays.length; j++) {
22418                             way = memberWays[j];
22419                             if (way.contains(nodeIDs[0])) { n0 = nodeIDs[0]; }
22420                             if (way.contains(nodeIDs[1])) { n1 = nodeIDs[1]; }
22421                         }
22422
22423                         if (n0 && n1) {    // both nodes are part of the restriction
22424                             var ok = false;
22425                             for (j = 0; j < memberWays.length; j++) {
22426                                 way = memberWays[j];
22427                                 if (way.areAdjacent(n0, n1)) {
22428                                     ok = true;
22429                                     break;
22430                                 }
22431                             }
22432                             if (!ok) {
22433                                 return 'restriction';
22434                             }
22435                         }
22436                     }
22437
22438                     // 2b. disable if nodes being connected will destroy a member way in a restriction
22439                     // (to test, make a copy and try actually connecting the nodes)
22440                     for (j = 0; j < memberWays.length; j++) {
22441                         way = memberWays[j].update({});   // make copy
22442                         for (k = 0; k < nodeIDs.length; k++) {
22443                             if (nodeIDs[k] === survivor.id) { continue; }
22444
22445                             if (way.areAdjacent(nodeIDs[k], survivor.id)) {
22446                                 way = way.removeNode(nodeIDs[k]);
22447                             } else {
22448                                 way = way.replaceNode(nodeIDs[k], survivor.id);
22449                             }
22450                         }
22451                         if (way.isDegenerate()) {
22452                             return 'restriction';
22453                         }
22454                     }
22455                 }
22456
22457                 return false;
22458
22459
22460                 // if a key node appears multiple times (indexOf !== lastIndexOf) it's a FROM-VIA or TO-VIA junction
22461                 function hasDuplicates(n, i, arr) {
22462                     return arr.indexOf(n) !== arr.lastIndexOf(n);
22463                 }
22464
22465                 function keyNodeFilter(froms, tos) {
22466                     return function(n) {
22467                         return froms.indexOf(n) === -1 && tos.indexOf(n) === -1;
22468                     };
22469                 }
22470
22471                 function collectNodes(member, collection) {
22472                     var entity = graph.hasEntity(member.id);
22473                     if (!entity) { return; }
22474
22475                     var role = member.role || '';
22476                     if (!collection[role]) {
22477                         collection[role] = [];
22478                     }
22479
22480                     if (member.type === 'node') {
22481                         collection[role].push(member.id);
22482                         if (role === 'via') {
22483                             collection.keyfrom.push(member.id);
22484                             collection.keyto.push(member.id);
22485                         }
22486
22487                     } else if (member.type === 'way') {
22488                         collection[role].push.apply(collection[role], entity.nodes);
22489                         if (role === 'from' || role === 'via') {
22490                             collection.keyfrom.push(entity.first());
22491                             collection.keyfrom.push(entity.last());
22492                         }
22493                         if (role === 'to' || role === 'via') {
22494                             collection.keyto.push(entity.first());
22495                             collection.keyto.push(entity.last());
22496                         }
22497                     }
22498                 }
22499             };
22500
22501
22502             return action;
22503         }
22504
22505         function actionCopyEntities(ids, fromGraph) {
22506             var _copies = {};
22507
22508
22509             var action = function(graph) {
22510                 ids.forEach(function(id) {
22511                     fromGraph.entity(id).copy(fromGraph, _copies);
22512                 });
22513
22514                 for (var id in _copies) {
22515                     graph = graph.replace(_copies[id]);
22516                 }
22517
22518                 return graph;
22519             };
22520
22521
22522             action.copies = function() {
22523                 return _copies;
22524             };
22525
22526
22527             return action;
22528         }
22529
22530         function actionDeleteMember(relationId, memberIndex) {
22531             return function(graph) {
22532                 var relation = graph.entity(relationId)
22533                     .removeMember(memberIndex);
22534
22535                 graph = graph.replace(relation);
22536
22537                 if (relation.isDegenerate())
22538                     { graph = actionDeleteRelation(relation.id)(graph); }
22539
22540                 return graph;
22541             };
22542         }
22543
22544         function actionDiscardTags(difference, discardTags) {
22545           discardTags = discardTags || {};
22546
22547           return function (graph) {
22548             difference.modified().forEach(checkTags);
22549             difference.created().forEach(checkTags);
22550             return graph;
22551
22552             function checkTags(entity) {
22553               var keys = Object.keys(entity.tags);
22554               var didDiscard = false;
22555               var tags = {};
22556
22557               for (var i = 0; i < keys.length; i++) {
22558                 var k = keys[i];
22559                 if (discardTags[k] || !entity.tags[k]) {
22560                   didDiscard = true;
22561                 } else {
22562                   tags[k] = entity.tags[k];
22563                 }
22564               }
22565               if (didDiscard) {
22566                 graph = graph.replace(entity.update({ tags: tags }));
22567               }
22568             }
22569
22570           };
22571         }
22572
22573         // Disconnect the ways at the given node.
22574         //
22575         // Optionally, disconnect only the given ways.
22576         //
22577         // For testing convenience, accepts an ID to assign to the (first) new node.
22578         // Normally, this will be undefined and the way will automatically
22579         // be assigned a new ID.
22580         //
22581         // This is the inverse of `iD.actionConnect`.
22582         //
22583         // Reference:
22584         //   https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/UnjoinNodeAction.as
22585         //   https://github.com/openstreetmap/josm/blob/mirror/src/org/openstreetmap/josm/actions/UnGlueAction.java
22586         //
22587         function actionDisconnect(nodeId, newNodeId) {
22588             var wayIds;
22589
22590
22591             var action = function(graph) {
22592                 var node = graph.entity(nodeId);
22593                 var connections = action.connections(graph);
22594
22595                 connections.forEach(function(connection) {
22596                     var way = graph.entity(connection.wayID);
22597                     var newNode = osmNode({id: newNodeId, loc: node.loc, tags: node.tags});
22598
22599                     graph = graph.replace(newNode);
22600                     if (connection.index === 0 && way.isArea()) {
22601                         // replace shared node with shared node..
22602                         graph = graph.replace(way.replaceNode(way.nodes[0], newNode.id));
22603                     } else if (way.isClosed() && connection.index === way.nodes.length - 1) {
22604                         // replace closing node with new new node..
22605                         graph = graph.replace(way.unclose().addNode(newNode.id));
22606                     } else {
22607                         // replace shared node with multiple new nodes..
22608                         graph = graph.replace(way.updateNode(newNode.id, connection.index));
22609                     }
22610                 });
22611
22612                 return graph;
22613             };
22614
22615
22616             action.connections = function(graph) {
22617                 var candidates = [];
22618                 var keeping = false;
22619                 var parentWays = graph.parentWays(graph.entity(nodeId));
22620                 var way, waynode;
22621                 for (var i = 0; i < parentWays.length; i++) {
22622                     way = parentWays[i];
22623                     if (wayIds && wayIds.indexOf(way.id) === -1) {
22624                         keeping = true;
22625                         continue;
22626                     }
22627                     if (way.isArea() && (way.nodes[0] === nodeId)) {
22628                         candidates.push({ wayID: way.id, index: 0 });
22629                     } else {
22630                         for (var j = 0; j < way.nodes.length; j++) {
22631                             waynode = way.nodes[j];
22632                             if (waynode === nodeId) {
22633                                 if (way.isClosed() &&
22634                                     parentWays.length > 1 &&
22635                                     wayIds &&
22636                                     wayIds.indexOf(way.id) !== -1 &&
22637                                     j === way.nodes.length - 1) {
22638                                     continue;
22639                                 }
22640                                 candidates.push({ wayID: way.id, index: j });
22641                             }
22642                         }
22643                     }
22644                 }
22645
22646                 return keeping ? candidates : candidates.slice(1);
22647             };
22648
22649
22650             action.disabled = function(graph) {
22651                 var connections = action.connections(graph);
22652                 if (connections.length === 0)
22653                     { return 'not_connected'; }
22654
22655                 var parentWays = graph.parentWays(graph.entity(nodeId));
22656                 var seenRelationIds = {};
22657                 var sharedRelation;
22658
22659                 parentWays.forEach(function(way) {
22660                     var relations = graph.parentRelations(way);
22661                     relations.forEach(function(relation) {
22662                         if (relation.id in seenRelationIds) {
22663                             if (wayIds) {
22664                                 if (wayIds.indexOf(way.id) !== -1 ||
22665                                     wayIds.indexOf(seenRelationIds[relation.id]) !== -1) {
22666                                     sharedRelation = relation;
22667                                 }
22668                             } else {
22669                                 sharedRelation = relation;
22670                             }
22671                         } else {
22672                             seenRelationIds[relation.id] = way.id;
22673                         }
22674                     });
22675                 });
22676
22677                 if (sharedRelation)
22678                     { return 'relation'; }
22679             };
22680
22681
22682             action.limitWays = function(val) {
22683                 if (!arguments.length) { return wayIds; }
22684                 wayIds = val;
22685                 return action;
22686             };
22687
22688
22689             return action;
22690         }
22691
22692         function actionExtract(entityID) {
22693
22694             var extractedNodeID;
22695
22696             var action = function(graph) {
22697                 var entity = graph.entity(entityID);
22698
22699                 if (entity.type === 'node') {
22700                     return extractFromNode(entity, graph);
22701                 }
22702
22703                 return extractFromWayOrRelation(entity, graph);
22704             };
22705
22706             function extractFromNode(node, graph) {
22707
22708                 extractedNodeID = node.id;
22709
22710                 // Create a new node to replace the one we will detach
22711                 var replacement = osmNode({ loc: node.loc });
22712                 graph = graph.replace(replacement);
22713
22714                 // Process each way in turn, updating the graph as we go
22715                 graph = graph.parentWays(node)
22716                     .reduce(function(accGraph, parentWay) {
22717                         return accGraph.replace(parentWay.replaceNode(entityID, replacement.id));
22718                     }, graph);
22719
22720                 // Process any relations too
22721                 return graph.parentRelations(node)
22722                     .reduce(function(accGraph, parentRel) {
22723                         return accGraph.replace(parentRel.replaceMember(node, replacement));
22724                     }, graph);
22725             }
22726
22727             function extractFromWayOrRelation(entity, graph) {
22728
22729                 var fromGeometry = entity.geometry(graph);
22730
22731                 var keysToCopyAndRetain = ['source', 'wheelchair'];
22732                 var keysToRetain = ['area'];
22733                 var buildingKeysToRetain = ['architect', 'building', 'height', 'layer'];
22734
22735                 var extractedLoc = d3_geoCentroid(entity.asGeoJSON(graph));
22736                 if (!extractedLoc  || !isFinite(extractedLoc[0]) || !isFinite(extractedLoc[1])) {
22737                     extractedLoc = entity.extent(graph).center();
22738                 }
22739
22740                 var indoorAreaValues = {
22741                     area: true,
22742                     corridor: true,
22743                     elevator: true,
22744                     level: true,
22745                     room: true
22746                 };
22747
22748                 var isBuilding = (entity.tags.building && entity.tags.building !== 'no') ||
22749                     (entity.tags['building:part'] && entity.tags['building:part'] !== 'no');
22750
22751                 var isIndoorArea = fromGeometry === 'area' && entity.tags.indoor && indoorAreaValues[entity.tags.indoor];
22752
22753                 var entityTags = Object.assign({}, entity.tags);  // shallow copy
22754                 var pointTags = {};
22755                 for (var key in entityTags) {
22756
22757                     if (entity.type === 'relation' &&
22758                         key === 'type') {
22759                         continue;
22760                     }
22761
22762                     if (keysToRetain.indexOf(key) !== -1) {
22763                         continue;
22764                     }
22765
22766                     if (isBuilding) {
22767                         // don't transfer building-related tags
22768                         if (buildingKeysToRetain.indexOf(key) !== -1 ||
22769                             key.match(/^building:.{1,}/) ||
22770                             key.match(/^roof:.{1,}/)) { continue; }
22771                     }
22772                     // leave `indoor` tag on the area
22773                     if (isIndoorArea && key === 'indoor') {
22774                         continue;
22775                     }
22776
22777                     // copy the tag from the entity to the point
22778                     pointTags[key] = entityTags[key];
22779
22780                     // leave addresses and some other tags so they're on both features
22781                     if (keysToCopyAndRetain.indexOf(key) !== -1 ||
22782                         key.match(/^addr:.{1,}/)) {
22783                         continue;
22784                     } else if (isIndoorArea && key === 'level') {
22785                         // leave `level` on both features
22786                         continue;
22787                     }
22788
22789                     // remove the tag from the entity
22790                     delete entityTags[key];
22791                 }
22792
22793                 if (!isBuilding && !isIndoorArea && fromGeometry === 'area') {
22794                     // ensure that areas keep area geometry
22795                     entityTags.area = 'yes';
22796                 }
22797
22798                 var replacement = osmNode({ loc: extractedLoc, tags: pointTags });
22799                 graph = graph.replace(replacement);
22800
22801                 extractedNodeID = replacement.id;
22802
22803                 return graph.replace(entity.update({tags: entityTags}));
22804             }
22805
22806             action.getExtractedNodeID = function() {
22807                 return extractedNodeID;
22808             };
22809
22810             return action;
22811         }
22812
22813         // Join ways at the end node they share.
22814         //
22815         // This is the inverse of `iD.actionSplit`.
22816         //
22817         // Reference:
22818         //   https://github.com/systemed/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/MergeWaysAction.as
22819         //   https://github.com/openstreetmap/josm/blob/mirror/src/org/openstreetmap/josm/actions/CombineWayAction.java
22820         //
22821         function actionJoin(ids) {
22822
22823             function groupEntitiesByGeometry(graph) {
22824                 var entities = ids.map(function(id) { return graph.entity(id); });
22825                 return Object.assign(
22826                     { line: [] },
22827                     utilArrayGroupBy(entities, function(entity) { return entity.geometry(graph); })
22828                 );
22829             }
22830
22831
22832             var action = function(graph) {
22833                 var ways = ids.map(graph.entity, graph);
22834                 var survivorID = ways[0].id;
22835
22836                 // if any of the ways are sided (e.g. coastline, cliff, kerb)
22837                 // sort them first so they establish the overall order - #6033
22838                 ways.sort(function(a, b) {
22839                     var aSided = a.isSided();
22840                     var bSided = b.isSided();
22841                     return (aSided && !bSided) ? -1
22842                         : (bSided && !aSided) ? 1
22843                         : 0;
22844                 });
22845
22846                 // Prefer to keep an existing way.
22847                 for (var i = 0; i < ways.length; i++) {
22848                     if (!ways[i].isNew()) {
22849                         survivorID = ways[i].id;
22850                         break;
22851                     }
22852                 }
22853
22854                 var sequences = osmJoinWays(ways, graph);
22855                 var joined = sequences[0];
22856
22857                 // We might need to reverse some of these ways before joining them.  #4688
22858                 // `joined.actions` property will contain any actions we need to apply.
22859                 graph = sequences.actions.reduce(function(g, action) { return action(g); }, graph);
22860
22861                 var survivor = graph.entity(survivorID);
22862                 survivor = survivor.update({ nodes: joined.nodes.map(function(n) { return n.id; }) });
22863                 graph = graph.replace(survivor);
22864
22865                 joined.forEach(function(way) {
22866                     if (way.id === survivorID) { return; }
22867
22868                     graph.parentRelations(way).forEach(function(parent) {
22869                         graph = graph.replace(parent.replaceMember(way, survivor));
22870                     });
22871
22872                     survivor = survivor.mergeTags(way.tags);
22873
22874                     graph = graph.replace(survivor);
22875                     graph = actionDeleteWay(way.id)(graph);
22876                 });
22877
22878                 // Finds if the join created a single-member multipolygon,
22879                 // and if so turns it into a basic area instead
22880                 function checkForSimpleMultipolygon() {
22881                     if (!survivor.isClosed()) { return; }
22882
22883                     var multipolygons = graph.parentMultipolygons(survivor).filter(function(multipolygon) {
22884                         // find multipolygons where the survivor is the only member
22885                         return multipolygon.members.length === 1;
22886                     });
22887
22888                     // skip if this is the single member of multiple multipolygons
22889                     if (multipolygons.length !== 1) { return; }
22890
22891                     var multipolygon = multipolygons[0];
22892
22893                     for (var key in survivor.tags) {
22894                         if (multipolygon.tags[key] &&
22895                             // don't collapse if tags cannot be cleanly merged
22896                             multipolygon.tags[key] !== survivor.tags[key]) { return; }
22897                     }
22898
22899                     survivor = survivor.mergeTags(multipolygon.tags);
22900                     graph = graph.replace(survivor);
22901                     graph = actionDeleteRelation(multipolygon.id, true /* allow untagged members */)(graph);
22902
22903                     var tags = Object.assign({}, survivor.tags);
22904                     if (survivor.geometry(graph) !== 'area') {
22905                         // ensure the feature persists as an area
22906                         tags.area = 'yes';
22907                     }
22908                     delete tags.type; // remove type=multipolygon
22909                     survivor = survivor.update({ tags: tags });
22910                     graph = graph.replace(survivor);
22911                 }
22912                 checkForSimpleMultipolygon();
22913
22914                 return graph;
22915             };
22916
22917             // Returns the number of nodes the resultant way is expected to have
22918             action.resultingWayNodesLength = function(graph) {
22919                 return ids.reduce(function(count, id) {
22920                     return count + graph.entity(id).nodes.length;
22921                 }, 0) - ids.length - 1;
22922             };
22923
22924
22925             action.disabled = function(graph) {
22926                 var geometries = groupEntitiesByGeometry(graph);
22927                 if (ids.length < 2 || ids.length !== geometries.line.length) {
22928                     return 'not_eligible';
22929                 }
22930
22931                 var joined = osmJoinWays(ids.map(graph.entity, graph), graph);
22932                 if (joined.length > 1) {
22933                     return 'not_adjacent';
22934                 }
22935
22936                 // Loop through all combinations of path-pairs
22937                 // to check potential intersections between all pairs
22938                 for (var i = 0; i < ids.length - 1; i++) {
22939                     for (var j = i + 1; j < ids.length; j++) {
22940                         var path1 = graph.childNodes(graph.entity(ids[i]))
22941                             .map(function(e) { return e.loc; });
22942                         var path2 = graph.childNodes(graph.entity(ids[j]))
22943                             .map(function(e) { return e.loc; });
22944                         var intersections = geoPathIntersections(path1, path2);
22945
22946                         // Check if intersections are just nodes lying on top of
22947                         // each other/the line, as opposed to crossing it
22948                         var common = utilArrayIntersection(
22949                             joined[0].nodes.map(function(n) { return n.loc.toString(); }),
22950                             intersections.map(function(n) { return n.toString(); })
22951                         );
22952                         if (common.length !== intersections.length) {
22953                             return 'paths_intersect';
22954                         }
22955                     }
22956                 }
22957
22958                 var nodeIds = joined[0].nodes.map(function(n) { return n.id; }).slice(1, -1);
22959                 var relation;
22960                 var tags = {};
22961                 var conflicting = false;
22962
22963                 joined[0].forEach(function(way) {
22964                     var parents = graph.parentRelations(way);
22965                     parents.forEach(function(parent) {
22966                         if (parent.isRestriction() && parent.members.some(function(m) { return nodeIds.indexOf(m.id) >= 0; })) {
22967                             relation = parent;
22968                         }
22969                     });
22970
22971                     for (var k in way.tags) {
22972                         if (!(k in tags)) {
22973                             tags[k] = way.tags[k];
22974                         } else if (tags[k] && osmIsInterestingTag(k) && tags[k] !== way.tags[k]) {
22975                             conflicting = true;
22976                         }
22977                     }
22978                 });
22979
22980                 if (relation) {
22981                     return 'restriction';
22982                 }
22983
22984                 if (conflicting) {
22985                     return 'conflicting_tags';
22986                 }
22987             };
22988
22989
22990             return action;
22991         }
22992
22993         function actionMerge(ids) {
22994
22995             function groupEntitiesByGeometry(graph) {
22996                 var entities = ids.map(function(id) { return graph.entity(id); });
22997                 return Object.assign(
22998                     { point: [], area: [], line: [], relation: [] },
22999                     utilArrayGroupBy(entities, function(entity) { return entity.geometry(graph); })
23000                 );
23001             }
23002
23003
23004             var action = function(graph) {
23005                 var geometries = groupEntitiesByGeometry(graph);
23006                 var target = geometries.area[0] || geometries.line[0];
23007                 var points = geometries.point;
23008
23009                 points.forEach(function(point) {
23010                     target = target.mergeTags(point.tags);
23011                     graph = graph.replace(target);
23012
23013                     graph.parentRelations(point).forEach(function(parent) {
23014                         graph = graph.replace(parent.replaceMember(point, target));
23015                     });
23016
23017                     var nodes = utilArrayUniq(graph.childNodes(target));
23018                     var removeNode = point;
23019
23020                     for (var i = 0; i < nodes.length; i++) {
23021                         var node = nodes[i];
23022                         if (graph.parentWays(node).length > 1 ||
23023                             graph.parentRelations(node).length ||
23024                             node.hasInterestingTags()) {
23025                             continue;
23026                         }
23027
23028                         // Found an uninteresting child node on the target way.
23029                         // Move orig point into its place to preserve point's history. #3683
23030                         graph = graph.replace(point.update({ tags: {}, loc: node.loc }));
23031                         target = target.replaceNode(node.id, point.id);
23032                         graph = graph.replace(target);
23033                         removeNode = node;
23034                         break;
23035                     }
23036
23037                     graph = graph.remove(removeNode);
23038                 });
23039
23040                 if (target.tags.area === 'yes') {
23041                     var tags = Object.assign({}, target.tags); // shallow copy
23042                     delete tags.area;
23043                     if (osmTagSuggestingArea(tags)) {
23044                         // remove the `area` tag if area geometry is now implied - #3851
23045                         target = target.update({ tags: tags });
23046                         graph = graph.replace(target);
23047                     }
23048                 }
23049
23050                 return graph;
23051             };
23052
23053
23054             action.disabled = function(graph) {
23055                 var geometries = groupEntitiesByGeometry(graph);
23056                 if (geometries.point.length === 0 ||
23057                     (geometries.area.length + geometries.line.length) !== 1 ||
23058                     geometries.relation.length !== 0) {
23059                     return 'not_eligible';
23060                 }
23061             };
23062
23063
23064             return action;
23065         }
23066
23067         // `actionMergeNodes` is just a combination of:
23068         //
23069         // 1. move all the nodes to a common location
23070         // 2. `actionConnect` them
23071
23072         function actionMergeNodes(nodeIDs, loc) {
23073
23074             // If there is a single "interesting" node, use that as the location.
23075             // Otherwise return the average location of all the nodes.
23076             function chooseLoc(graph) {
23077                 if (!nodeIDs.length) { return null; }
23078                 var sum = [0,0];
23079                 var interestingCount = 0;
23080                 var interestingLoc;
23081
23082                 for (var i = 0; i < nodeIDs.length; i++) {
23083                     var node = graph.entity(nodeIDs[i]);
23084                     if (node.hasInterestingTags()) {
23085                         interestingLoc = (++interestingCount === 1) ? node.loc : null;
23086                     }
23087                     sum = geoVecAdd(sum, node.loc);
23088                 }
23089
23090                 return interestingLoc || geoVecScale(sum, 1 / nodeIDs.length);
23091             }
23092
23093
23094             var action = function(graph) {
23095                 if (nodeIDs.length < 2) { return graph; }
23096                 var toLoc = loc;
23097                 if (!toLoc) {
23098                     toLoc = chooseLoc(graph);
23099                 }
23100
23101                 for (var i = 0; i < nodeIDs.length; i++) {
23102                     var node = graph.entity(nodeIDs[i]);
23103                     if (node.loc !== toLoc) {
23104                         graph = graph.replace(node.move(toLoc));
23105                     }
23106                 }
23107
23108                 return actionConnect(nodeIDs)(graph);
23109             };
23110
23111
23112             action.disabled = function(graph) {
23113                 if (nodeIDs.length < 2) { return 'not_eligible'; }
23114
23115                 for (var i = 0; i < nodeIDs.length; i++) {
23116                     var entity = graph.entity(nodeIDs[i]);
23117                     if (entity.type !== 'node') { return 'not_eligible'; }
23118                 }
23119
23120                 return actionConnect(nodeIDs).disabled(graph);
23121             };
23122
23123             return action;
23124         }
23125
23126         function osmChangeset() {
23127             if (!(this instanceof osmChangeset)) {
23128                 return (new osmChangeset()).initialize(arguments);
23129             } else if (arguments.length) {
23130                 this.initialize(arguments);
23131             }
23132         }
23133
23134
23135         osmEntity.changeset = osmChangeset;
23136
23137         osmChangeset.prototype = Object.create(osmEntity.prototype);
23138
23139         Object.assign(osmChangeset.prototype, {
23140
23141             type: 'changeset',
23142
23143
23144             extent: function() {
23145                 return new geoExtent();
23146             },
23147
23148
23149             geometry: function() {
23150                 return 'changeset';
23151             },
23152
23153
23154             asJXON: function() {
23155                 return {
23156                     osm: {
23157                         changeset: {
23158                             tag: Object.keys(this.tags).map(function(k) {
23159                                 return { '@k': k, '@v': this.tags[k] };
23160                             }, this),
23161                             '@version': 0.6,
23162                             '@generator': 'iD'
23163                         }
23164                     }
23165                 };
23166             },
23167
23168
23169             // Generate [osmChange](http://wiki.openstreetmap.org/wiki/OsmChange)
23170             // XML. Returns a string.
23171             osmChangeJXON: function(changes) {
23172                 var changeset_id = this.id;
23173
23174                 function nest(x, order) {
23175                     var groups = {};
23176                     for (var i = 0; i < x.length; i++) {
23177                         var tagName = Object.keys(x[i])[0];
23178                         if (!groups[tagName]) { groups[tagName] = []; }
23179                         groups[tagName].push(x[i][tagName]);
23180                     }
23181                     var ordered = {};
23182                     order.forEach(function(o) {
23183                         if (groups[o]) { ordered[o] = groups[o]; }
23184                     });
23185                     return ordered;
23186                 }
23187
23188
23189                 // sort relations in a changeset by dependencies
23190                 function sort(changes) {
23191
23192                     // find a referenced relation in the current changeset
23193                     function resolve(item) {
23194                         return relations.find(function(relation) {
23195                             return item.keyAttributes.type === 'relation'
23196                                 && item.keyAttributes.ref === relation['@id'];
23197                         });
23198                     }
23199
23200                     // a new item is an item that has not been already processed
23201                     function isNew(item) {
23202                         return !sorted[ item['@id'] ] && !processing.find(function(proc) {
23203                             return proc['@id'] === item['@id'];
23204                         });
23205                     }
23206
23207                     var processing = [];
23208                     var sorted = {};
23209                     var relations = changes.relation;
23210
23211                     if (!relations) { return changes; }
23212
23213                     for (var i = 0; i < relations.length; i++) {
23214                         var relation = relations[i];
23215
23216                         // skip relation if already sorted
23217                         if (!sorted[relation['@id']]) {
23218                             processing.push(relation);
23219                         }
23220
23221                         while (processing.length > 0) {
23222                             var next = processing[0],
23223                             deps = next.member.map(resolve).filter(Boolean).filter(isNew);
23224                             if (deps.length === 0) {
23225                                 sorted[next['@id']] = next;
23226                                 processing.shift();
23227                             } else {
23228                                 processing = deps.concat(processing);
23229                             }
23230                         }
23231                     }
23232
23233                     changes.relation = Object.values(sorted);
23234                     return changes;
23235                 }
23236
23237                 function rep(entity) {
23238                     return entity.asJXON(changeset_id);
23239                 }
23240
23241                 return {
23242                     osmChange: {
23243                         '@version': 0.6,
23244                         '@generator': 'iD',
23245                         'create': sort(nest(changes.created.map(rep), ['node', 'way', 'relation'])),
23246                         'modify': nest(changes.modified.map(rep), ['node', 'way', 'relation']),
23247                         'delete': Object.assign(nest(changes.deleted.map(rep), ['relation', 'way', 'node']), { '@if-unused': true })
23248                     }
23249                 };
23250             },
23251
23252
23253             asGeoJSON: function() {
23254                 return {};
23255             }
23256
23257         });
23258
23259         function osmNote() {
23260             if (!(this instanceof osmNote)) {
23261                 return (new osmNote()).initialize(arguments);
23262             } else if (arguments.length) {
23263                 this.initialize(arguments);
23264             }
23265         }
23266
23267
23268         osmNote.id = function() {
23269             return osmNote.id.next--;
23270         };
23271
23272
23273         osmNote.id.next = -1;
23274
23275
23276         Object.assign(osmNote.prototype, {
23277
23278             type: 'note',
23279
23280             initialize: function(sources) {
23281                 for (var i = 0; i < sources.length; ++i) {
23282                     var source = sources[i];
23283                     for (var prop in source) {
23284                         if (Object.prototype.hasOwnProperty.call(source, prop)) {
23285                             if (source[prop] === undefined) {
23286                                 delete this[prop];
23287                             } else {
23288                                 this[prop] = source[prop];
23289                             }
23290                         }
23291                     }
23292                 }
23293
23294                 if (!this.id) {
23295                     this.id = osmNote.id() + '';  // as string
23296                 }
23297
23298                 return this;
23299             },
23300
23301             extent: function() {
23302                 return new geoExtent(this.loc);
23303             },
23304
23305             update: function(attrs) {
23306                 return osmNote(this, attrs); // {v: 1 + (this.v || 0)}
23307             },
23308
23309             isNew: function() {
23310                 return this.id < 0;
23311             },
23312
23313             move: function(loc) {
23314                 return this.update({ loc: loc });
23315             }
23316
23317         });
23318
23319         function osmRelation() {
23320             if (!(this instanceof osmRelation)) {
23321                 return (new osmRelation()).initialize(arguments);
23322             } else if (arguments.length) {
23323                 this.initialize(arguments);
23324             }
23325         }
23326
23327
23328         osmEntity.relation = osmRelation;
23329
23330         osmRelation.prototype = Object.create(osmEntity.prototype);
23331
23332
23333         osmRelation.creationOrder = function(a, b) {
23334             var aId = parseInt(osmEntity.id.toOSM(a.id), 10);
23335             var bId = parseInt(osmEntity.id.toOSM(b.id), 10);
23336
23337             if (aId < 0 || bId < 0) { return aId - bId; }
23338             return bId - aId;
23339         };
23340
23341
23342         Object.assign(osmRelation.prototype, {
23343             type: 'relation',
23344             members: [],
23345
23346
23347             copy: function(resolver, copies) {
23348                 if (copies[this.id]) { return copies[this.id]; }
23349
23350                 var copy = osmEntity.prototype.copy.call(this, resolver, copies);
23351
23352                 var members = this.members.map(function(member) {
23353                     return Object.assign({}, member, { id: resolver.entity(member.id).copy(resolver, copies).id });
23354                 });
23355
23356                 copy = copy.update({members: members});
23357                 copies[this.id] = copy;
23358
23359                 return copy;
23360             },
23361
23362
23363             extent: function(resolver, memo) {
23364                 return resolver.transient(this, 'extent', function() {
23365                     if (memo && memo[this.id]) { return geoExtent(); }
23366                     memo = memo || {};
23367                     memo[this.id] = true;
23368
23369                     var extent = geoExtent();
23370                     for (var i = 0; i < this.members.length; i++) {
23371                         var member = resolver.hasEntity(this.members[i].id);
23372                         if (member) {
23373                             extent._extend(member.extent(resolver, memo));
23374                         }
23375                     }
23376                     return extent;
23377                 });
23378             },
23379
23380
23381             geometry: function(graph) {
23382                 return graph.transient(this, 'geometry', function() {
23383                     return this.isMultipolygon() ? 'area' : 'relation';
23384                 });
23385             },
23386
23387
23388             isDegenerate: function() {
23389                 return this.members.length === 0;
23390             },
23391
23392
23393             // Return an array of members, each extended with an 'index' property whose value
23394             // is the member index.
23395             indexedMembers: function() {
23396                 var result = new Array(this.members.length);
23397                 for (var i = 0; i < this.members.length; i++) {
23398                     result[i] = Object.assign({}, this.members[i], {index: i});
23399                 }
23400                 return result;
23401             },
23402
23403
23404             // Return the first member with the given role. A copy of the member object
23405             // is returned, extended with an 'index' property whose value is the member index.
23406             memberByRole: function(role) {
23407                 for (var i = 0; i < this.members.length; i++) {
23408                     if (this.members[i].role === role) {
23409                         return Object.assign({}, this.members[i], {index: i});
23410                     }
23411                 }
23412             },
23413
23414             // Same as memberByRole, but returns all members with the given role
23415             membersByRole: function(role) {
23416                 var result = [];
23417                 for (var i = 0; i < this.members.length; i++) {
23418                     if (this.members[i].role === role) {
23419                         result.push(Object.assign({}, this.members[i], {index: i}));
23420                     }
23421                 }
23422                 return result;
23423             },
23424
23425             // Return the first member with the given id. A copy of the member object
23426             // is returned, extended with an 'index' property whose value is the member index.
23427             memberById: function(id) {
23428                 for (var i = 0; i < this.members.length; i++) {
23429                     if (this.members[i].id === id) {
23430                         return Object.assign({}, this.members[i], {index: i});
23431                     }
23432                 }
23433             },
23434
23435
23436             // Return the first member with the given id and role. A copy of the member object
23437             // is returned, extended with an 'index' property whose value is the member index.
23438             memberByIdAndRole: function(id, role) {
23439                 for (var i = 0; i < this.members.length; i++) {
23440                     if (this.members[i].id === id && this.members[i].role === role) {
23441                         return Object.assign({}, this.members[i], {index: i});
23442                     }
23443                 }
23444             },
23445
23446
23447             addMember: function(member, index) {
23448                 var members = this.members.slice();
23449                 members.splice(index === undefined ? members.length : index, 0, member);
23450                 return this.update({members: members});
23451             },
23452
23453
23454             updateMember: function(member, index) {
23455                 var members = this.members.slice();
23456                 members.splice(index, 1, Object.assign({}, members[index], member));
23457                 return this.update({members: members});
23458             },
23459
23460
23461             removeMember: function(index) {
23462                 var members = this.members.slice();
23463                 members.splice(index, 1);
23464                 return this.update({members: members});
23465             },
23466
23467
23468             removeMembersWithID: function(id) {
23469                 var members = this.members.filter(function(m) { return m.id !== id; });
23470                 return this.update({members: members});
23471             },
23472
23473             moveMember: function(fromIndex, toIndex) {
23474                 var members = this.members.slice();
23475                 members.splice(toIndex, 0, members.splice(fromIndex, 1)[0]);
23476                 return this.update({members: members});
23477             },
23478
23479
23480             // Wherever a member appears with id `needle.id`, replace it with a member
23481             // with id `replacement.id`, type `replacement.type`, and the original role,
23482             // By default, adding a duplicate member (by id and role) is prevented.
23483             // Return an updated relation.
23484             replaceMember: function(needle, replacement, keepDuplicates) {
23485                 if (!this.memberById(needle.id)) { return this; }
23486
23487                 var members = [];
23488
23489                 for (var i = 0; i < this.members.length; i++) {
23490                     var member = this.members[i];
23491                     if (member.id !== needle.id) {
23492                         members.push(member);
23493                     } else if (keepDuplicates || !this.memberByIdAndRole(replacement.id, member.role)) {
23494                         members.push({ id: replacement.id, type: replacement.type, role: member.role });
23495                     }
23496                 }
23497
23498                 return this.update({ members: members });
23499             },
23500
23501
23502             asJXON: function(changeset_id) {
23503                 var r = {
23504                     relation: {
23505                         '@id': this.osmId(),
23506                         '@version': this.version || 0,
23507                         member: this.members.map(function(member) {
23508                             return {
23509                                 keyAttributes: {
23510                                     type: member.type,
23511                                     role: member.role,
23512                                     ref: osmEntity.id.toOSM(member.id)
23513                                 }
23514                             };
23515                         }, this),
23516                         tag: Object.keys(this.tags).map(function(k) {
23517                             return { keyAttributes: { k: k, v: this.tags[k] } };
23518                         }, this)
23519                     }
23520                 };
23521                 if (changeset_id) {
23522                     r.relation['@changeset'] = changeset_id;
23523                 }
23524                 return r;
23525             },
23526
23527
23528             asGeoJSON: function(resolver) {
23529                 return resolver.transient(this, 'GeoJSON', function () {
23530                     if (this.isMultipolygon()) {
23531                         return {
23532                             type: 'MultiPolygon',
23533                             coordinates: this.multipolygon(resolver)
23534                         };
23535                     } else {
23536                         return {
23537                             type: 'FeatureCollection',
23538                             properties: this.tags,
23539                             features: this.members.map(function (member) {
23540                                 return Object.assign({role: member.role}, resolver.entity(member.id).asGeoJSON(resolver));
23541                             })
23542                         };
23543                     }
23544                 });
23545             },
23546
23547
23548             area: function(resolver) {
23549                 return resolver.transient(this, 'area', function() {
23550                     return d3_geoArea(this.asGeoJSON(resolver));
23551                 });
23552             },
23553
23554
23555             isMultipolygon: function() {
23556                 return this.tags.type === 'multipolygon';
23557             },
23558
23559
23560             isComplete: function(resolver) {
23561                 for (var i = 0; i < this.members.length; i++) {
23562                     if (!resolver.hasEntity(this.members[i].id)) {
23563                         return false;
23564                     }
23565                 }
23566                 return true;
23567             },
23568
23569
23570             hasFromViaTo: function() {
23571                 return (
23572                     this.members.some(function(m) { return m.role === 'from'; }) &&
23573                     this.members.some(function(m) { return m.role === 'via'; }) &&
23574                     this.members.some(function(m) { return m.role === 'to'; })
23575                 );
23576             },
23577
23578
23579             isRestriction: function() {
23580                 return !!(this.tags.type && this.tags.type.match(/^restriction:?/));
23581             },
23582
23583
23584             isValidRestriction: function() {
23585                 if (!this.isRestriction()) { return false; }
23586
23587                 var froms = this.members.filter(function(m) { return m.role === 'from'; });
23588                 var vias = this.members.filter(function(m) { return m.role === 'via'; });
23589                 var tos = this.members.filter(function(m) { return m.role === 'to'; });
23590
23591                 if (froms.length !== 1 && this.tags.restriction !== 'no_entry') { return false; }
23592                 if (froms.some(function(m) { return m.type !== 'way'; })) { return false; }
23593
23594                 if (tos.length !== 1 && this.tags.restriction !== 'no_exit') { return false; }
23595                 if (tos.some(function(m) { return m.type !== 'way'; })) { return false; }
23596
23597                 if (vias.length === 0) { return false; }
23598                 if (vias.length > 1 && vias.some(function(m) { return m.type !== 'way'; })) { return false; }
23599
23600                 return true;
23601             },
23602
23603
23604             // Returns an array [A0, ... An], each Ai being an array of node arrays [Nds0, ... Ndsm],
23605             // where Nds0 is an outer ring and subsequent Ndsi's (if any i > 0) being inner rings.
23606             //
23607             // This corresponds to the structure needed for rendering a multipolygon path using a
23608             // `evenodd` fill rule, as well as the structure of a GeoJSON MultiPolygon geometry.
23609             //
23610             // In the case of invalid geometries, this function will still return a result which
23611             // includes the nodes of all way members, but some Nds may be unclosed and some inner
23612             // rings not matched with the intended outer ring.
23613             //
23614             multipolygon: function(resolver) {
23615                 var outers = this.members.filter(function(m) { return 'outer' === (m.role || 'outer'); });
23616                 var inners = this.members.filter(function(m) { return 'inner' === m.role; });
23617
23618                 outers = osmJoinWays(outers, resolver);
23619                 inners = osmJoinWays(inners, resolver);
23620
23621                 var sequenceToLineString = function(sequence) {
23622                     if (sequence.nodes.length > 2 &&
23623                         sequence.nodes[0] !== sequence.nodes[sequence.nodes.length - 1]) {
23624                         // close unclosed parts to ensure correct area rendering - #2945
23625                         sequence.nodes.push(sequence.nodes[0]);
23626                     }
23627                     return sequence.nodes.map(function(node) { return node.loc; });
23628                 };
23629
23630                 outers = outers.map(sequenceToLineString);
23631                 inners = inners.map(sequenceToLineString);
23632
23633                 var result = outers.map(function(o) {
23634                     // Heuristic for detecting counterclockwise winding order. Assumes
23635                     // that OpenStreetMap polygons are not hemisphere-spanning.
23636                     return [d3_geoArea({ type: 'Polygon', coordinates: [o] }) > 2 * Math.PI ? o.reverse() : o];
23637                 });
23638
23639                 function findOuter(inner) {
23640                     var o, outer;
23641
23642                     for (o = 0; o < outers.length; o++) {
23643                         outer = outers[o];
23644                         if (geoPolygonContainsPolygon(outer, inner))
23645                             { return o; }
23646                     }
23647
23648                     for (o = 0; o < outers.length; o++) {
23649                         outer = outers[o];
23650                         if (geoPolygonIntersectsPolygon(outer, inner, false))
23651                             { return o; }
23652                     }
23653                 }
23654
23655                 for (var i = 0; i < inners.length; i++) {
23656                     var inner = inners[i];
23657
23658                     if (d3_geoArea({ type: 'Polygon', coordinates: [inner] }) < 2 * Math.PI) {
23659                         inner = inner.reverse();
23660                     }
23661
23662                     var o = findOuter(inners[i]);
23663                     if (o !== undefined) {
23664                         result[o].push(inners[i]);
23665                     } else {
23666                         result.push([inners[i]]); // Invalid geometry
23667                     }
23668                 }
23669
23670                 return result;
23671             }
23672         });
23673
23674         var QAItem = function QAItem(loc, service, itemType, id, props) {
23675           // Store required properties
23676           this.loc = loc;
23677           this.service = service.title;
23678           this.itemType = itemType;
23679
23680           // All issues must have an ID for selection, use generic if none specified
23681           this.id = id ? id : ("" + (QAItem.id()));
23682
23683           this.update(props);
23684
23685           // Some QA services have marker icons to differentiate issues
23686           if (service && typeof service.getIcon === 'function') {
23687             this.icon = service.getIcon(itemType);
23688           }
23689
23690           return this;
23691         };
23692
23693         QAItem.prototype.update = function update (props) {
23694             var this$1 = this;
23695
23696           // You can't override this initial information
23697           var ref = this;
23698             var loc = ref.loc;
23699             var service = ref.service;
23700             var itemType = ref.itemType;
23701             var id = ref.id;
23702
23703           Object.keys(props).forEach(function (prop) { return this$1[prop] = props[prop]; });
23704
23705           this.loc = loc;
23706           this.service = service;
23707           this.itemType = itemType;
23708           this.id = id;
23709
23710           return this;
23711         };
23712
23713         // Generic handling for newly created QAItems
23714         QAItem.id = function id () {
23715           return this.nextId--;
23716         };
23717         QAItem.nextId = -1;
23718
23719         // Split a way at the given node.
23720         //
23721         // Optionally, split only the given ways, if multiple ways share
23722         // the given node.
23723         //
23724         // This is the inverse of `iD.actionJoin`.
23725         //
23726         // For testing convenience, accepts an ID to assign to the new way.
23727         // Normally, this will be undefined and the way will automatically
23728         // be assigned a new ID.
23729         //
23730         // Reference:
23731         //   https://github.com/systemed/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/SplitWayAction.as
23732         //
23733         function actionSplit(nodeId, newWayIds) {
23734             var _wayIDs;
23735
23736             // The IDs of the ways actually created by running this action
23737             var createdWayIDs = [];
23738
23739             // If the way is closed, we need to search for a partner node
23740             // to split the way at.
23741             //
23742             // The following looks for a node that is both far away from
23743             // the initial node in terms of way segment length and nearby
23744             // in terms of beeline-distance. This assures that areas get
23745             // split on the most "natural" points (independent of the number
23746             // of nodes).
23747             // For example: bone-shaped areas get split across their waist
23748             // line, circles across the diameter.
23749             function splitArea(nodes, idxA, graph) {
23750                 var lengths = new Array(nodes.length);
23751                 var length;
23752                 var i;
23753                 var best = 0;
23754                 var idxB;
23755
23756                 function wrap(index) {
23757                     return utilWrap(index, nodes.length);
23758                 }
23759
23760                 function dist(nA, nB) {
23761                     var locA = graph.entity(nA).loc;
23762                     var locB = graph.entity(nB).loc;
23763                     var epsilon = 1e-6;
23764                     return (locA && locB) ? geoSphericalDistance(locA, locB) : epsilon;
23765                 }
23766
23767                 // calculate lengths
23768                 length = 0;
23769                 for (i = wrap(idxA + 1); i !== idxA; i = wrap(i + 1)) {
23770                     length += dist(nodes[i], nodes[wrap(i - 1)]);
23771                     lengths[i] = length;
23772                 }
23773
23774                 length = 0;
23775                 for (i = wrap(idxA - 1); i !== idxA; i = wrap(i - 1)) {
23776                     length += dist(nodes[i], nodes[wrap(i + 1)]);
23777                     if (length < lengths[i]) {
23778                         lengths[i] = length;
23779                     }
23780                 }
23781
23782                 // determine best opposite node to split
23783                 for (i = 0; i < nodes.length; i++) {
23784                     var cost = lengths[i] / dist(nodes[idxA], nodes[i]);
23785                     if (cost > best) {
23786                         idxB = i;
23787                         best = cost;
23788                     }
23789                 }
23790
23791                 return idxB;
23792             }
23793
23794
23795             function split(graph, wayA, newWayId) {
23796                 var wayB = osmWay({ id: newWayId, tags: wayA.tags });   // `wayB` is the NEW way
23797                 var origNodes = wayA.nodes.slice();
23798                 var nodesA;
23799                 var nodesB;
23800                 var isArea = wayA.isArea();
23801                 var isOuter = osmIsOldMultipolygonOuterMember(wayA, graph);
23802
23803                 if (wayA.isClosed()) {
23804                     var nodes = wayA.nodes.slice(0, -1);
23805                     var idxA = nodes.indexOf(nodeId);
23806                     var idxB = splitArea(nodes, idxA, graph);
23807
23808                     if (idxB < idxA) {
23809                         nodesA = nodes.slice(idxA).concat(nodes.slice(0, idxB + 1));
23810                         nodesB = nodes.slice(idxB, idxA + 1);
23811                     } else {
23812                         nodesA = nodes.slice(idxA, idxB + 1);
23813                         nodesB = nodes.slice(idxB).concat(nodes.slice(0, idxA + 1));
23814                     }
23815                 } else {
23816                     var idx = wayA.nodes.indexOf(nodeId, 1);
23817                     nodesA = wayA.nodes.slice(0, idx + 1);
23818                     nodesB = wayA.nodes.slice(idx);
23819                 }
23820
23821                 wayA = wayA.update({ nodes: nodesA });
23822                 wayB = wayB.update({ nodes: nodesB });
23823
23824                 graph = graph.replace(wayA);
23825                 graph = graph.replace(wayB);
23826
23827                 graph.parentRelations(wayA).forEach(function(relation) {
23828                     var member;
23829
23830                     // Turn restrictions - make sure:
23831                     // 1. Splitting a FROM/TO way - only `wayA` OR `wayB` remains in relation
23832                     //    (whichever one is connected to the VIA node/ways)
23833                     // 2. Splitting a VIA way - `wayB` remains in relation as a VIA way
23834                     if (relation.hasFromViaTo()) {
23835                         var f = relation.memberByRole('from');
23836                         var v = relation.membersByRole('via');
23837                         var t = relation.memberByRole('to');
23838                         var i;
23839
23840                         // 1. split a FROM/TO
23841                         if (f.id === wayA.id || t.id === wayA.id) {
23842                             var keepB = false;
23843                             if (v.length === 1 && v[0].type === 'node') {   // check via node
23844                                 keepB = wayB.contains(v[0].id);
23845                             } else {                                        // check via way(s)
23846                                 for (i = 0; i < v.length; i++) {
23847                                     if (v[i].type === 'way') {
23848                                         var wayVia = graph.hasEntity(v[i].id);
23849                                         if (wayVia && utilArrayIntersection(wayB.nodes, wayVia.nodes).length) {
23850                                             keepB = true;
23851                                             break;
23852                                         }
23853                                     }
23854                                 }
23855                             }
23856
23857                             if (keepB) {
23858                                 relation = relation.replaceMember(wayA, wayB);
23859                                 graph = graph.replace(relation);
23860                             }
23861
23862                         // 2. split a VIA
23863                         } else {
23864                             for (i = 0; i < v.length; i++) {
23865                                 if (v[i].type === 'way' && v[i].id === wayA.id) {
23866                                     member = {
23867                                         id: wayB.id,
23868                                         type: 'way',
23869                                         role: 'via'
23870                                     };
23871                                     graph = actionAddMember(relation.id, member, v[i].index + 1)(graph);
23872                                     break;
23873                                 }
23874                             }
23875                         }
23876
23877                     // All other relations (Routes, Multipolygons, etc):
23878                     // 1. Both `wayA` and `wayB` remain in the relation
23879                     // 2. But must be inserted as a pair (see `actionAddMember` for details)
23880                     } else {
23881                         if (relation === isOuter) {
23882                             graph = graph.replace(relation.mergeTags(wayA.tags));
23883                             graph = graph.replace(wayA.update({ tags: {} }));
23884                             graph = graph.replace(wayB.update({ tags: {} }));
23885                         }
23886
23887                         member = {
23888                             id: wayB.id,
23889                             type: 'way',
23890                             role: relation.memberById(wayA.id).role
23891                         };
23892
23893                         var insertPair = {
23894                             originalID: wayA.id,
23895                             insertedID: wayB.id,
23896                             nodes: origNodes
23897                         };
23898
23899                         graph = actionAddMember(relation.id, member, undefined, insertPair)(graph);
23900                     }
23901                 });
23902
23903                 if (!isOuter && isArea) {
23904                     var multipolygon = osmRelation({
23905                         tags: Object.assign({}, wayA.tags, { type: 'multipolygon' }),
23906                         members: [
23907                             { id: wayA.id, role: 'outer', type: 'way' },
23908                             { id: wayB.id, role: 'outer', type: 'way' }
23909                         ]
23910                     });
23911
23912                     graph = graph.replace(multipolygon);
23913                     graph = graph.replace(wayA.update({ tags: {} }));
23914                     graph = graph.replace(wayB.update({ tags: {} }));
23915                 }
23916
23917                 createdWayIDs.push(wayB.id);
23918
23919                 return graph;
23920             }
23921
23922             var action = function(graph) {
23923                 var candidates = action.ways(graph);
23924                 createdWayIDs = [];
23925                 for (var i = 0; i < candidates.length; i++) {
23926                     graph = split(graph, candidates[i], newWayIds && newWayIds[i]);
23927                 }
23928                 return graph;
23929             };
23930
23931             action.getCreatedWayIDs = function() {
23932                 return createdWayIDs;
23933             };
23934
23935             action.ways = function(graph) {
23936                 var node = graph.entity(nodeId);
23937                 var parents = graph.parentWays(node);
23938                 var hasLines = parents.some(function(parent) {
23939                     return parent.geometry(graph) === 'line';
23940                 });
23941
23942                 return parents.filter(function(parent) {
23943                     if (_wayIDs && _wayIDs.indexOf(parent.id) === -1)
23944                         { return false; }
23945
23946                     if (!_wayIDs && hasLines && parent.geometry(graph) !== 'line')
23947                         { return false; }
23948
23949                     if (parent.isClosed()) {
23950                         return true;
23951                     }
23952
23953                     for (var i = 1; i < parent.nodes.length - 1; i++) {
23954                         if (parent.nodes[i] === nodeId) {
23955                             return true;
23956                         }
23957                     }
23958
23959                     return false;
23960                 });
23961             };
23962
23963
23964             action.disabled = function(graph) {
23965                 var candidates = action.ways(graph);
23966                 if (candidates.length === 0 || (_wayIDs && _wayIDs.length !== candidates.length)) {
23967                     return 'not_eligible';
23968                 }
23969             };
23970
23971
23972             action.limitWays = function(val) {
23973                 if (!arguments.length) { return _wayIDs; }
23974                 _wayIDs = val;
23975                 return action;
23976             };
23977
23978
23979             return action;
23980         }
23981
23982         function coreGraph(other, mutable) {
23983             if (!(this instanceof coreGraph)) { return new coreGraph(other, mutable); }
23984
23985             if (other instanceof coreGraph) {
23986                 var base = other.base();
23987                 this.entities = Object.assign(Object.create(base.entities), other.entities);
23988                 this._parentWays = Object.assign(Object.create(base.parentWays), other._parentWays);
23989                 this._parentRels = Object.assign(Object.create(base.parentRels), other._parentRels);
23990
23991             } else {
23992                 this.entities = Object.create({});
23993                 this._parentWays = Object.create({});
23994                 this._parentRels = Object.create({});
23995                 this.rebase(other || [], [this]);
23996             }
23997
23998             this.transients = {};
23999             this._childNodes = {};
24000             this.frozen = !mutable;
24001         }
24002
24003
24004         coreGraph.prototype = {
24005
24006             hasEntity: function(id) {
24007                 return this.entities[id];
24008             },
24009
24010
24011             entity: function(id) {
24012                 var entity = this.entities[id];
24013
24014                 //https://github.com/openstreetmap/iD/issues/3973#issuecomment-307052376
24015                 if (!entity) {
24016                     entity = this.entities.__proto__[id];  // eslint-disable-line no-proto
24017                 }
24018
24019                 if (!entity) {
24020                     throw new Error('entity ' + id + ' not found');
24021                 }
24022                 return entity;
24023             },
24024
24025
24026             geometry: function(id) {
24027                 return this.entity(id).geometry(this);
24028             },
24029
24030
24031             transient: function(entity, key, fn) {
24032                 var id = entity.id;
24033                 var transients = this.transients[id] || (this.transients[id] = {});
24034
24035                 if (transients[key] !== undefined) {
24036                     return transients[key];
24037                 }
24038
24039                 transients[key] = fn.call(entity);
24040
24041                 return transients[key];
24042             },
24043
24044
24045             parentWays: function(entity) {
24046                 var parents = this._parentWays[entity.id];
24047                 var result = [];
24048                 if (parents) {
24049                     parents.forEach(function(id) {
24050                         result.push(this.entity(id));
24051                     }, this);
24052                 }
24053                 return result;
24054             },
24055
24056
24057             isPoi: function(entity) {
24058                 var parents = this._parentWays[entity.id];
24059                 return !parents || parents.size === 0;
24060             },
24061
24062
24063             isShared: function(entity) {
24064                 var parents = this._parentWays[entity.id];
24065                 return parents && parents.size > 1;
24066             },
24067
24068
24069             parentRelations: function(entity) {
24070                 var parents = this._parentRels[entity.id];
24071                 var result = [];
24072                 if (parents) {
24073                     parents.forEach(function(id) {
24074                         result.push(this.entity(id));
24075                     }, this);
24076                 }
24077                 return result;
24078             },
24079
24080             parentMultipolygons: function(entity) {
24081                 return this.parentRelations(entity).filter(function(relation) {
24082                     return relation.isMultipolygon();
24083                 });
24084             },
24085
24086
24087             childNodes: function(entity) {
24088                 if (this._childNodes[entity.id]) { return this._childNodes[entity.id]; }
24089                 if (!entity.nodes) { return []; }
24090
24091                 var nodes = [];
24092                 for (var i = 0; i < entity.nodes.length; i++) {
24093                     nodes[i] = this.entity(entity.nodes[i]);
24094                 }
24095
24096                 this._childNodes[entity.id] = nodes;
24097                 return this._childNodes[entity.id];
24098             },
24099
24100
24101             base: function() {
24102                 return {
24103                     'entities': Object.getPrototypeOf(this.entities),
24104                     'parentWays': Object.getPrototypeOf(this._parentWays),
24105                     'parentRels': Object.getPrototypeOf(this._parentRels)
24106                 };
24107             },
24108
24109
24110             // Unlike other graph methods, rebase mutates in place. This is because it
24111             // is used only during the history operation that merges newly downloaded
24112             // data into each state. To external consumers, it should appear as if the
24113             // graph always contained the newly downloaded data.
24114             rebase: function(entities, stack, force) {
24115                 var base = this.base();
24116                 var i, j, k, id;
24117
24118                 for (i = 0; i < entities.length; i++) {
24119                     var entity = entities[i];
24120
24121                     if (!entity.visible || (!force && base.entities[entity.id]))
24122                         { continue; }
24123
24124                     // Merging data into the base graph
24125                     base.entities[entity.id] = entity;
24126                     this._updateCalculated(undefined, entity, base.parentWays, base.parentRels);
24127
24128                     // Restore provisionally-deleted nodes that are discovered to have an extant parent
24129                     if (entity.type === 'way') {
24130                         for (j = 0; j < entity.nodes.length; j++) {
24131                             id = entity.nodes[j];
24132                             for (k = 1; k < stack.length; k++) {
24133                                 var ents = stack[k].entities;
24134                                 if (ents.hasOwnProperty(id) && ents[id] === undefined) {
24135                                     delete ents[id];
24136                                 }
24137                             }
24138                         }
24139                     }
24140                 }
24141
24142                 for (i = 0; i < stack.length; i++) {
24143                     stack[i]._updateRebased();
24144                 }
24145             },
24146
24147
24148             _updateRebased: function() {
24149                 var base = this.base();
24150
24151                 Object.keys(this._parentWays).forEach(function(child) {
24152                     if (base.parentWays[child]) {
24153                         base.parentWays[child].forEach(function(id) {
24154                             if (!this.entities.hasOwnProperty(id)) {
24155                                 this._parentWays[child].add(id);
24156                             }
24157                         }, this);
24158                     }
24159                 }, this);
24160
24161                 Object.keys(this._parentRels).forEach(function(child) {
24162                     if (base.parentRels[child]) {
24163                         base.parentRels[child].forEach(function(id) {
24164                             if (!this.entities.hasOwnProperty(id)) {
24165                                 this._parentRels[child].add(id);
24166                             }
24167                         }, this);
24168                     }
24169                 }, this);
24170
24171                 this.transients = {};
24172
24173                 // this._childNodes is not updated, under the assumption that
24174                 // ways are always downloaded with their child nodes.
24175             },
24176
24177
24178             // Updates calculated properties (parentWays, parentRels) for the specified change
24179             _updateCalculated: function(oldentity, entity, parentWays, parentRels) {
24180                 parentWays = parentWays || this._parentWays;
24181                 parentRels = parentRels || this._parentRels;
24182
24183                 var type = entity && entity.type || oldentity && oldentity.type;
24184                 var removed, added, i;
24185
24186                 if (type === 'way') {   // Update parentWays
24187                     if (oldentity && entity) {
24188                         removed = utilArrayDifference(oldentity.nodes, entity.nodes);
24189                         added = utilArrayDifference(entity.nodes, oldentity.nodes);
24190                     } else if (oldentity) {
24191                         removed = oldentity.nodes;
24192                         added = [];
24193                     } else if (entity) {
24194                         removed = [];
24195                         added = entity.nodes;
24196                     }
24197                     for (i = 0; i < removed.length; i++) {
24198                         // make a copy of prototype property, store as own property, and update..
24199                         parentWays[removed[i]] = new Set(parentWays[removed[i]]);
24200                         parentWays[removed[i]].delete(oldentity.id);
24201                     }
24202                     for (i = 0; i < added.length; i++) {
24203                         // make a copy of prototype property, store as own property, and update..
24204                         parentWays[added[i]] = new Set(parentWays[added[i]]);
24205                         parentWays[added[i]].add(entity.id);
24206                     }
24207
24208                 } else if (type === 'relation') {   // Update parentRels
24209
24210                     // diff only on the IDs since the same entity can be a member multiple times with different roles
24211                     var oldentityMemberIDs = oldentity ? oldentity.members.map(function(m) { return m.id; }) : [];
24212                     var entityMemberIDs = entity ? entity.members.map(function(m) { return m.id; }) : [];
24213
24214                     if (oldentity && entity) {
24215                         removed = utilArrayDifference(oldentityMemberIDs, entityMemberIDs);
24216                         added = utilArrayDifference(entityMemberIDs, oldentityMemberIDs);
24217                     } else if (oldentity) {
24218                         removed = oldentityMemberIDs;
24219                         added = [];
24220                     } else if (entity) {
24221                         removed = [];
24222                         added = entityMemberIDs;
24223                     }
24224                     for (i = 0; i < removed.length; i++) {
24225                         // make a copy of prototype property, store as own property, and update..
24226                         parentRels[removed[i]] = new Set(parentRels[removed[i]]);
24227                         parentRels[removed[i]].delete(oldentity.id);
24228                     }
24229                     for (i = 0; i < added.length; i++) {
24230                         // make a copy of prototype property, store as own property, and update..
24231                         parentRels[added[i]] = new Set(parentRels[added[i]]);
24232                         parentRels[added[i]].add(entity.id);
24233                     }
24234                 }
24235             },
24236
24237
24238             replace: function(entity) {
24239                 if (this.entities[entity.id] === entity) { return this; }
24240
24241                 return this.update(function() {
24242                     this._updateCalculated(this.entities[entity.id], entity);
24243                     this.entities[entity.id] = entity;
24244                 });
24245             },
24246
24247
24248             remove: function(entity) {
24249                 return this.update(function() {
24250                     this._updateCalculated(entity, undefined);
24251                     this.entities[entity.id] = undefined;
24252                 });
24253             },
24254
24255
24256             revert: function(id) {
24257                 var baseEntity = this.base().entities[id];
24258                 var headEntity = this.entities[id];
24259                 if (headEntity === baseEntity) { return this; }
24260
24261                 return this.update(function() {
24262                     this._updateCalculated(headEntity, baseEntity);
24263                     delete this.entities[id];
24264                 });
24265             },
24266
24267
24268             update: function() {
24269                 var arguments$1 = arguments;
24270
24271                 var graph = this.frozen ? coreGraph(this, true) : this;
24272                 for (var i = 0; i < arguments.length; i++) {
24273                     arguments$1[i].call(graph, graph);
24274                 }
24275
24276                 if (this.frozen) { graph.frozen = true; }
24277
24278                 return graph;
24279             },
24280
24281
24282             // Obliterates any existing entities
24283             load: function(entities) {
24284                 var base = this.base();
24285                 this.entities = Object.create(base.entities);
24286
24287                 for (var i in entities) {
24288                     this.entities[i] = entities[i];
24289                     this._updateCalculated(base.entities[i], this.entities[i]);
24290                 }
24291
24292                 return this;
24293             }
24294         };
24295
24296         function osmTurn(turn) {
24297             if (!(this instanceof osmTurn)) {
24298                 return new osmTurn(turn);
24299             }
24300             Object.assign(this, turn);
24301         }
24302
24303
24304         function osmIntersection(graph, startVertexId, maxDistance) {
24305             maxDistance = maxDistance || 30;    // in meters
24306             var vgraph = coreGraph();           // virtual graph
24307             var i, j, k;
24308
24309
24310             function memberOfRestriction(entity) {
24311                 return graph.parentRelations(entity)
24312                     .some(function(r) { return r.isRestriction(); });
24313             }
24314
24315             function isRoad(way) {
24316                 if (way.isArea() || way.isDegenerate()) { return false; }
24317                 var roads = {
24318                     'motorway': true,
24319                     'motorway_link': true,
24320                     'trunk': true,
24321                     'trunk_link': true,
24322                     'primary': true,
24323                     'primary_link': true,
24324                     'secondary': true,
24325                     'secondary_link': true,
24326                     'tertiary': true,
24327                     'tertiary_link': true,
24328                     'residential': true,
24329                     'unclassified': true,
24330                     'living_street': true,
24331                     'service': true,
24332                     'road': true,
24333                     'track': true
24334                 };
24335                 return roads[way.tags.highway];
24336             }
24337
24338
24339             var startNode = graph.entity(startVertexId);
24340             var checkVertices = [startNode];
24341             var checkWays;
24342             var vertices = [];
24343             var vertexIds = [];
24344             var vertex;
24345             var ways = [];
24346             var wayIds = [];
24347             var way;
24348             var nodes = [];
24349             var node;
24350             var parents = [];
24351             var parent;
24352
24353             // `actions` will store whatever actions must be performed to satisfy
24354             // preconditions for adding a turn restriction to this intersection.
24355             //  - Remove any existing degenerate turn restrictions (missing from/to, etc)
24356             //  - Reverse oneways so that they are drawn in the forward direction
24357             //  - Split ways on key vertices
24358             var actions = [];
24359
24360
24361             // STEP 1:  walk the graph outwards from starting vertex to search
24362             //  for more key vertices and ways to include in the intersection..
24363
24364             while (checkVertices.length) {
24365                 vertex = checkVertices.pop();
24366
24367                 // check this vertex for parent ways that are roads
24368                 checkWays = graph.parentWays(vertex);
24369                 var hasWays = false;
24370                 for (i = 0; i < checkWays.length; i++) {
24371                     way = checkWays[i];
24372                     if (!isRoad(way) && !memberOfRestriction(way)) { continue; }
24373
24374                     ways.push(way);   // it's a road, or it's already in a turn restriction
24375                     hasWays = true;
24376
24377                     // check the way's children for more key vertices
24378                     nodes = utilArrayUniq(graph.childNodes(way));
24379                     for (j = 0; j < nodes.length; j++) {
24380                         node = nodes[j];
24381                         if (node === vertex) { continue; }                                           // same thing
24382                         if (vertices.indexOf(node) !== -1) { continue; }                             // seen it already
24383                         if (geoSphericalDistance(node.loc, startNode.loc) > maxDistance) { continue; }   // too far from start
24384
24385                         // a key vertex will have parents that are also roads
24386                         var hasParents = false;
24387                         parents = graph.parentWays(node);
24388                         for (k = 0; k < parents.length; k++) {
24389                             parent = parents[k];
24390                             if (parent === way) { continue; }                 // same thing
24391                             if (ways.indexOf(parent) !== -1) { continue; }    // seen it already
24392                             if (!isRoad(parent)) { continue; }                // not a road
24393                             hasParents = true;
24394                             break;
24395                         }
24396
24397                         if (hasParents) {
24398                             checkVertices.push(node);
24399                         }
24400                     }
24401                 }
24402
24403                 if (hasWays) {
24404                     vertices.push(vertex);
24405                 }
24406             }
24407
24408             vertices = utilArrayUniq(vertices);
24409             ways = utilArrayUniq(ways);
24410
24411
24412             // STEP 2:  Build a virtual graph containing only the entities in the intersection..
24413             // Everything done after this step should act on the virtual graph
24414             // Any actions that must be performed later to the main graph go in `actions` array
24415             ways.forEach(function(way) {
24416                 graph.childNodes(way).forEach(function(node) {
24417                     vgraph = vgraph.replace(node);
24418                 });
24419
24420                 vgraph = vgraph.replace(way);
24421
24422                 graph.parentRelations(way).forEach(function(relation) {
24423                     if (relation.isRestriction()) {
24424                         if (relation.isValidRestriction(graph)) {
24425                             vgraph = vgraph.replace(relation);
24426                         } else if (relation.isComplete(graph)) {
24427                             actions.push(actionDeleteRelation(relation.id));
24428                         }
24429                     }
24430                 });
24431             });
24432
24433
24434             // STEP 3:  Force all oneways to be drawn in the forward direction
24435             ways.forEach(function(w) {
24436                 var way = vgraph.entity(w.id);
24437                 if (way.tags.oneway === '-1') {
24438                     var action = actionReverse(way.id, { reverseOneway: true });
24439                     actions.push(action);
24440                     vgraph = action(vgraph);
24441                 }
24442             });
24443
24444
24445             // STEP 4:  Split ways on key vertices
24446             var origCount = osmEntity.id.next.way;
24447             vertices.forEach(function(v) {
24448                 // This is an odd way to do it, but we need to find all the ways that
24449                 // will be split here, then split them one at a time to ensure that these
24450                 // actions can be replayed on the main graph exactly in the same order.
24451                 // (It is unintuitive, but the order of ways returned from graph.parentWays()
24452                 // is arbitrary, depending on how the main graph and vgraph were built)
24453                 var splitAll = actionSplit(v.id);
24454                 if (!splitAll.disabled(vgraph)) {
24455                     splitAll.ways(vgraph).forEach(function(way) {
24456                         var splitOne = actionSplit(v.id).limitWays([way.id]);
24457                         actions.push(splitOne);
24458                         vgraph = splitOne(vgraph);
24459                     });
24460                 }
24461             });
24462
24463             // In here is where we should also split the intersection at nearby junction.
24464             //   for https://github.com/mapbox/iD-internal/issues/31
24465             // nearbyVertices.forEach(function(v) {
24466             // });
24467
24468             // Reasons why we reset the way id count here:
24469             //  1. Continuity with way ids created by the splits so that we can replay
24470             //     these actions later if the user decides to create a turn restriction
24471             //  2. Avoids churning way ids just by hovering over a vertex
24472             //     and displaying the turn restriction editor
24473             osmEntity.id.next.way = origCount;
24474
24475
24476             // STEP 5:  Update arrays to point to vgraph entities
24477             vertexIds = vertices.map(function(v) { return v.id; });
24478             vertices = [];
24479             ways = [];
24480
24481             vertexIds.forEach(function(id) {
24482                 var vertex = vgraph.entity(id);
24483                 var parents = vgraph.parentWays(vertex);
24484                 vertices.push(vertex);
24485                 ways = ways.concat(parents);
24486             });
24487
24488             vertices = utilArrayUniq(vertices);
24489             ways = utilArrayUniq(ways);
24490
24491             vertexIds = vertices.map(function(v) { return v.id; });
24492             wayIds = ways.map(function(w) { return w.id; });
24493
24494
24495             // STEP 6:  Update the ways with some metadata that will be useful for
24496             // walking the intersection graph later and rendering turn arrows.
24497
24498             function withMetadata(way, vertexIds) {
24499                 var __oneWay = way.isOneWay();
24500
24501                 // which affixes are key vertices?
24502                 var __first = (vertexIds.indexOf(way.first()) !== -1);
24503                 var __last = (vertexIds.indexOf(way.last()) !== -1);
24504
24505                 // what roles is this way eligible for?
24506                 var __via = (__first && __last);
24507                 var __from = ((__first && !__oneWay) || __last);
24508                 var __to = (__first || (__last && !__oneWay));
24509
24510                 return way.update({
24511                     __first:  __first,
24512                     __last:  __last,
24513                     __from:  __from,
24514                     __via: __via,
24515                     __to:  __to,
24516                     __oneWay:  __oneWay
24517                 });
24518             }
24519
24520             ways = [];
24521             wayIds.forEach(function(id) {
24522                 var way = withMetadata(vgraph.entity(id), vertexIds);
24523                 vgraph = vgraph.replace(way);
24524                 ways.push(way);
24525             });
24526
24527
24528             // STEP 7:  Simplify - This is an iterative process where we:
24529             //  1. Find trivial vertices with only 2 parents
24530             //  2. trim off the leaf way from those vertices and remove from vgraph
24531
24532             var keepGoing;
24533             var removeWayIds = [];
24534             var removeVertexIds = [];
24535
24536             do {
24537                 keepGoing = false;
24538                 checkVertices = vertexIds.slice();
24539
24540                 for (i = 0; i < checkVertices.length; i++) {
24541                     var vertexId = checkVertices[i];
24542                     vertex = vgraph.hasEntity(vertexId);
24543
24544                     if (!vertex) {
24545                         if (vertexIds.indexOf(vertexId) !== -1) {
24546                             vertexIds.splice(vertexIds.indexOf(vertexId), 1);   // stop checking this one
24547                         }
24548                         removeVertexIds.push(vertexId);
24549                         continue;
24550                     }
24551
24552                     parents = vgraph.parentWays(vertex);
24553                     if (parents.length < 3) {
24554                         if (vertexIds.indexOf(vertexId) !== -1) {
24555                             vertexIds.splice(vertexIds.indexOf(vertexId), 1);   // stop checking this one
24556                         }
24557                     }
24558
24559                     if (parents.length === 2) {     // vertex with 2 parents is trivial
24560                         var a = parents[0];
24561                         var b = parents[1];
24562                         var aIsLeaf = a && !a.__via;
24563                         var bIsLeaf = b && !b.__via;
24564                         var leaf, survivor;
24565
24566                         if (aIsLeaf && !bIsLeaf) {
24567                             leaf = a;
24568                             survivor = b;
24569                         } else if (!aIsLeaf && bIsLeaf) {
24570                             leaf = b;
24571                             survivor = a;
24572                         }
24573
24574                         if (leaf && survivor) {
24575                             survivor = withMetadata(survivor, vertexIds);      // update survivor way
24576                             vgraph = vgraph.replace(survivor).remove(leaf);    // update graph
24577                             removeWayIds.push(leaf.id);
24578                             keepGoing = true;
24579                         }
24580                     }
24581
24582                     parents = vgraph.parentWays(vertex);
24583
24584                     if (parents.length < 2) {     // vertex is no longer a key vertex
24585                         if (vertexIds.indexOf(vertexId) !== -1) {
24586                             vertexIds.splice(vertexIds.indexOf(vertexId), 1);   // stop checking this one
24587                         }
24588                         removeVertexIds.push(vertexId);
24589                         keepGoing = true;
24590                     }
24591
24592                     if (parents.length < 1) {     // vertex is no longer attached to anything
24593                         vgraph = vgraph.remove(vertex);
24594                     }
24595
24596                 }
24597             } while (keepGoing);
24598
24599
24600             vertices = vertices
24601                 .filter(function(vertex) { return removeVertexIds.indexOf(vertex.id) === -1; })
24602                 .map(function(vertex) { return vgraph.entity(vertex.id); });
24603             ways = ways
24604                 .filter(function(way) { return removeWayIds.indexOf(way.id) === -1; })
24605                 .map(function(way) { return vgraph.entity(way.id); });
24606
24607
24608             // OK!  Here is our intersection..
24609             var intersection = {
24610                 graph: vgraph,
24611                 actions: actions,
24612                 vertices: vertices,
24613                 ways: ways,
24614             };
24615
24616
24617
24618             // Get all the valid turns through this intersection given a starting way id.
24619             // This operates on the virtual graph for everything.
24620             //
24621             // Basically, walk through all possible paths from starting way,
24622             //   honoring the existing turn restrictions as we go (watch out for loops!)
24623             //
24624             // For each path found, generate and return a `osmTurn` datastructure.
24625             //
24626             intersection.turns = function(fromWayId, maxViaWay) {
24627                 if (!fromWayId) { return []; }
24628                 if (!maxViaWay) { maxViaWay = 0; }
24629
24630                 var vgraph = intersection.graph;
24631                 var keyVertexIds = intersection.vertices.map(function(v) { return v.id; });
24632
24633                 var start = vgraph.entity(fromWayId);
24634                 if (!start || !(start.__from || start.__via)) { return []; }
24635
24636                 // maxViaWay=0   from-*-to              (0 vias)
24637                 // maxViaWay=1   from-*-via-*-to        (1 via max)
24638                 // maxViaWay=2   from-*-via-*-via-*-to  (2 vias max)
24639                 var maxPathLength = (maxViaWay * 2) + 3;
24640                 var turns = [];
24641
24642                 step(start);
24643                 return turns;
24644
24645
24646                 // traverse the intersection graph and find all the valid paths
24647                 function step(entity, currPath, currRestrictions, matchedRestriction) {
24648                     currPath = (currPath || []).slice();  // shallow copy
24649                     if (currPath.length >= maxPathLength) { return; }
24650                     currPath.push(entity.id);
24651                     currRestrictions = (currRestrictions || []).slice();  // shallow copy
24652                     var i, j;
24653
24654                     if (entity.type === 'node') {
24655                         var parents = vgraph.parentWays(entity);
24656                         var nextWays = [];
24657
24658                         // which ways can we step into?
24659                         for (i = 0; i < parents.length; i++) {
24660                             var way = parents[i];
24661
24662                             // if next way is a oneway incoming to this vertex, skip
24663                             if (way.__oneWay && way.nodes[0] !== entity.id) { continue; }
24664
24665                             // if we have seen it before (allowing for an initial u-turn), skip
24666                             if (currPath.indexOf(way.id) !== -1 && currPath.length >= 3) { continue; }
24667
24668                             // Check all "current" restrictions (where we've already walked the `FROM`)
24669                             var restrict = undefined;
24670                             for (j = 0; j < currRestrictions.length; j++) {
24671                                 var restriction = currRestrictions[j];
24672                                 var f = restriction.memberByRole('from');
24673                                 var v = restriction.membersByRole('via');
24674                                 var t = restriction.memberByRole('to');
24675                                 var isOnly = /^only_/.test(restriction.tags.restriction);
24676
24677                                 // Does the current path match this turn restriction?
24678                                 var matchesFrom = (f.id === fromWayId);
24679                                 var matchesViaTo = false;
24680                                 var isAlongOnlyPath = false;
24681
24682                                 if (t.id === way.id) {     // match TO
24683
24684                                     if (v.length === 1 && v[0].type === 'node') {    // match VIA node
24685                                         matchesViaTo = (v[0].id === entity.id && (
24686                                             (matchesFrom && currPath.length === 2) ||
24687                                             (!matchesFrom && currPath.length > 2)
24688                                         ));
24689
24690                                     } else {                                         // match all VIA ways
24691                                         var pathVias = [];
24692                                         for (k = 2; k < currPath.length; k +=2 ) {   // k = 2 skips FROM
24693                                             pathVias.push(currPath[k]);              // (path goes way-node-way...)
24694                                         }
24695                                         var restrictionVias = [];
24696                                         for (k = 0; k < v.length; k++) {
24697                                             if (v[k].type === 'way') {
24698                                                 restrictionVias.push(v[k].id);
24699                                             }
24700                                         }
24701                                         var diff = utilArrayDifference(pathVias, restrictionVias);
24702                                         matchesViaTo = !diff.length;
24703                                     }
24704
24705                                 } else if (isOnly) {
24706                                     for (k = 0; k < v.length; k++) {
24707                                         // way doesn't match TO, but is one of the via ways along the path of an "only"
24708                                         if (v[k].type === 'way' && v[k].id === way.id) {
24709                                             isAlongOnlyPath = true;
24710                                             break;
24711                                         }
24712                                     }
24713                                 }
24714
24715                                 if (matchesViaTo) {
24716                                     if (isOnly) {
24717                                         restrict = { id: restriction.id, direct: matchesFrom, from: f.id, only: true, end: true };
24718                                     } else {
24719                                         restrict = { id: restriction.id, direct: matchesFrom, from: f.id, no: true, end: true };
24720                                     }
24721                                 } else {    // indirect - caused by a different nearby restriction
24722                                     if (isAlongOnlyPath) {
24723                                         restrict = { id: restriction.id, direct: false, from: f.id, only: true, end: false };
24724                                     } else if (isOnly) {
24725                                         restrict = { id: restriction.id, direct: false, from: f.id, no: true, end: true };
24726                                     }
24727                                 }
24728
24729                                 // stop looking if we find a "direct" restriction (matching FROM, VIA, TO)
24730                                 if (restrict && restrict.direct)
24731                                     { break; }
24732                             }
24733
24734                             nextWays.push({ way: way, restrict: restrict });
24735                         }
24736
24737                         nextWays.forEach(function(nextWay) {
24738                             step(nextWay.way, currPath, currRestrictions, nextWay.restrict);
24739                         });
24740
24741
24742                     } else {  // entity.type === 'way'
24743                         if (currPath.length >= 3) {     // this is a "complete" path..
24744                             var turnPath = currPath.slice();   // shallow copy
24745
24746                             // an indirect restriction - only include the partial path (starting at FROM)
24747                             if (matchedRestriction && matchedRestriction.direct === false) {
24748                                 for (i = 0; i < turnPath.length; i++) {
24749                                     if (turnPath[i] === matchedRestriction.from) {
24750                                         turnPath = turnPath.slice(i);
24751                                         break;
24752                                     }
24753                                 }
24754                             }
24755
24756                             var turn = pathToTurn(turnPath);
24757                             if (turn) {
24758                                 if (matchedRestriction) {
24759                                     turn.restrictionID = matchedRestriction.id;
24760                                     turn.no = matchedRestriction.no;
24761                                     turn.only = matchedRestriction.only;
24762                                     turn.direct = matchedRestriction.direct;
24763                                 }
24764                                 turns.push(osmTurn(turn));
24765                             }
24766
24767                             if (currPath[0] === currPath[2]) { return; }   // if we made a u-turn - stop here
24768                         }
24769
24770                         if (matchedRestriction && matchedRestriction.end) { return; }  // don't advance any further
24771
24772                         // which nodes can we step into?
24773                         var n1 = vgraph.entity(entity.first());
24774                         var n2 = vgraph.entity(entity.last());
24775                         var dist = geoSphericalDistance(n1.loc, n2.loc);
24776                         var nextNodes = [];
24777
24778                         if (currPath.length > 1) {
24779                             if (dist > maxDistance) { return; }   // the next node is too far
24780                             if (!entity.__via) { return; }        // this way is a leaf / can't be a via
24781                         }
24782
24783                         if (!entity.__oneWay &&                     // bidirectional..
24784                             keyVertexIds.indexOf(n1.id) !== -1 &&   // key vertex..
24785                             currPath.indexOf(n1.id) === -1) {       // haven't seen it yet..
24786                             nextNodes.push(n1);                     // can advance to first node
24787                         }
24788                         if (keyVertexIds.indexOf(n2.id) !== -1 &&   // key vertex..
24789                             currPath.indexOf(n2.id) === -1) {       // haven't seen it yet..
24790                             nextNodes.push(n2);                     // can advance to last node
24791                         }
24792
24793                         nextNodes.forEach(function(nextNode) {
24794                             // gather restrictions FROM this way
24795                             var fromRestrictions = vgraph.parentRelations(entity).filter(function(r) {
24796                                 if (!r.isRestriction()) { return false; }
24797
24798                                 var f = r.memberByRole('from');
24799                                 if (!f || f.id !== entity.id) { return false; }
24800
24801                                 var isOnly = /^only_/.test(r.tags.restriction);
24802                                 if (!isOnly) { return true; }
24803
24804                                 // `only_` restrictions only matter along the direction of the VIA - #4849
24805                                 var isOnlyVia = false;
24806                                 var v = r.membersByRole('via');
24807                                 if (v.length === 1 && v[0].type === 'node') {   // via node
24808                                     isOnlyVia = (v[0].id === nextNode.id);
24809                                 } else {                                        // via way(s)
24810                                     for (var i = 0; i < v.length; i++) {
24811                                         if (v[i].type !== 'way') { continue; }
24812                                         var viaWay = vgraph.entity(v[i].id);
24813                                         if (viaWay.first() === nextNode.id || viaWay.last() === nextNode.id) {
24814                                             isOnlyVia = true;
24815                                             break;
24816                                         }
24817                                     }
24818                                 }
24819                                 return isOnlyVia;
24820                             });
24821
24822                             step(nextNode, currPath, currRestrictions.concat(fromRestrictions), false);
24823                         });
24824                     }
24825                 }
24826
24827
24828                 // assumes path is alternating way-node-way of odd length
24829                 function pathToTurn(path) {
24830                     if (path.length < 3) { return; }
24831                     var fromWayId, fromNodeId, fromVertexId;
24832                     var toWayId, toNodeId, toVertexId;
24833                     var viaWayIds, viaNodeId, isUturn;
24834
24835                     fromWayId = path[0];
24836                     toWayId = path[path.length - 1];
24837
24838                     if (path.length === 3 && fromWayId === toWayId) {  // u turn
24839                         var way = vgraph.entity(fromWayId);
24840                         if (way.__oneWay) { return null; }
24841
24842                         isUturn = true;
24843                         viaNodeId = fromVertexId = toVertexId = path[1];
24844                         fromNodeId = toNodeId = adjacentNode(fromWayId, viaNodeId);
24845
24846                     } else {
24847                         isUturn = false;
24848                         fromVertexId = path[1];
24849                         fromNodeId = adjacentNode(fromWayId, fromVertexId);
24850                         toVertexId = path[path.length - 2];
24851                         toNodeId = adjacentNode(toWayId, toVertexId);
24852
24853                         if (path.length === 3) {
24854                             viaNodeId = path[1];
24855                         } else {
24856                             viaWayIds = path.filter(function(entityId) { return entityId[0] === 'w'; });
24857                             viaWayIds = viaWayIds.slice(1, viaWayIds.length - 1);  // remove first, last
24858                         }
24859                     }
24860
24861                     return {
24862                         key:  path.join('_'),
24863                         path: path,
24864                         from: { node: fromNodeId, way:  fromWayId, vertex: fromVertexId },
24865                         via:  { node: viaNodeId,  ways: viaWayIds },
24866                         to:   { node: toNodeId,   way:  toWayId, vertex: toVertexId },
24867                         u:    isUturn
24868                     };
24869
24870
24871                     function adjacentNode(wayId, affixId) {
24872                         var nodes = vgraph.entity(wayId).nodes;
24873                         return affixId === nodes[0] ? nodes[1] : nodes[nodes.length - 2];
24874                     }
24875                 }
24876
24877             };
24878
24879             return intersection;
24880         }
24881
24882
24883         function osmInferRestriction(graph, turn, projection) {
24884             var fromWay = graph.entity(turn.from.way);
24885             var fromNode = graph.entity(turn.from.node);
24886             var fromVertex = graph.entity(turn.from.vertex);
24887             var toWay = graph.entity(turn.to.way);
24888             var toNode = graph.entity(turn.to.node);
24889             var toVertex = graph.entity(turn.to.vertex);
24890
24891             var fromOneWay = (fromWay.tags.oneway === 'yes');
24892             var toOneWay = (toWay.tags.oneway === 'yes');
24893             var angle = (geoAngle(fromVertex, fromNode, projection) -
24894                         geoAngle(toVertex, toNode, projection)) * 180 / Math.PI;
24895
24896             while (angle < 0)
24897                 { angle += 360; }
24898
24899             if (fromNode === toNode)
24900                 { return 'no_u_turn'; }
24901             if ((angle < 23 || angle > 336) && fromOneWay && toOneWay)
24902                 { return 'no_u_turn'; }   // wider tolerance for u-turn if both ways are oneway
24903             if ((angle < 40 || angle > 319) && fromOneWay && toOneWay && turn.from.vertex !== turn.to.vertex)
24904                 { return 'no_u_turn'; }   // even wider tolerance for u-turn if there is a via way (from !== to)
24905             if (angle < 158)
24906                 { return 'no_right_turn'; }
24907             if (angle > 202)
24908                 { return 'no_left_turn'; }
24909
24910             return 'no_straight_on';
24911         }
24912
24913         function actionMergePolygon(ids, newRelationId) {
24914
24915             function groupEntities(graph) {
24916                 var entities = ids.map(function (id) { return graph.entity(id); });
24917                 var geometryGroups = utilArrayGroupBy(entities, function(entity) {
24918                     if (entity.type === 'way' && entity.isClosed()) {
24919                         return 'closedWay';
24920                     } else if (entity.type === 'relation' && entity.isMultipolygon()) {
24921                         return 'multipolygon';
24922                     } else {
24923                         return 'other';
24924                     }
24925                 });
24926
24927                 return Object.assign(
24928                     { closedWay: [], multipolygon: [], other: [] },
24929                     geometryGroups
24930                 );
24931             }
24932
24933
24934             var action = function(graph) {
24935                 var entities = groupEntities(graph);
24936
24937                 // An array representing all the polygons that are part of the multipolygon.
24938                 //
24939                 // Each element is itself an array of objects with an id property, and has a
24940                 // locs property which is an array of the locations forming the polygon.
24941                 var polygons = entities.multipolygon.reduce(function(polygons, m) {
24942                     return polygons.concat(osmJoinWays(m.members, graph));
24943                 }, []).concat(entities.closedWay.map(function(d) {
24944                     var member = [{id: d.id}];
24945                     member.nodes = graph.childNodes(d);
24946                     return member;
24947                 }));
24948
24949                 // contained is an array of arrays of boolean values,
24950                 // where contained[j][k] is true iff the jth way is
24951                 // contained by the kth way.
24952                 var contained = polygons.map(function(w, i) {
24953                     return polygons.map(function(d, n) {
24954                         if (i === n) { return null; }
24955                         return geoPolygonContainsPolygon(
24956                             d.nodes.map(function(n) { return n.loc; }),
24957                             w.nodes.map(function(n) { return n.loc; })
24958                         );
24959                     });
24960                 });
24961
24962                 // Sort all polygons as either outer or inner ways
24963                 var members = [];
24964                 var outer = true;
24965
24966                 while (polygons.length) {
24967                     extractUncontained(polygons);
24968                     polygons = polygons.filter(isContained);
24969                     contained = contained.filter(isContained).map(filterContained);
24970                 }
24971
24972                 function isContained(d, i) {
24973                     return contained[i].some(function(val) { return val; });
24974                 }
24975
24976                 function filterContained(d) {
24977                     return d.filter(isContained);
24978                 }
24979
24980                 function extractUncontained(polygons) {
24981                     polygons.forEach(function(d, i) {
24982                         if (!isContained(d, i)) {
24983                             d.forEach(function(member) {
24984                                 members.push({
24985                                     type: 'way',
24986                                     id: member.id,
24987                                     role: outer ? 'outer' : 'inner'
24988                                 });
24989                             });
24990                         }
24991                     });
24992                     outer = !outer;
24993                 }
24994
24995                 // Move all tags to one relation
24996                 var relation = entities.multipolygon[0] ||
24997                     osmRelation({ id: newRelationId, tags: { type: 'multipolygon' }});
24998
24999                 entities.multipolygon.slice(1).forEach(function(m) {
25000                     relation = relation.mergeTags(m.tags);
25001                     graph = graph.remove(m);
25002                 });
25003
25004                 entities.closedWay.forEach(function(way) {
25005                     function isThisOuter(m) {
25006                         return m.id === way.id && m.role !== 'inner';
25007                     }
25008                     if (members.some(isThisOuter)) {
25009                         relation = relation.mergeTags(way.tags);
25010                         graph = graph.replace(way.update({ tags: {} }));
25011                     }
25012                 });
25013
25014                 return graph.replace(relation.update({
25015                     members: members,
25016                     tags: utilObjectOmit(relation.tags, ['area'])
25017                 }));
25018             };
25019
25020
25021             action.disabled = function(graph) {
25022                 var entities = groupEntities(graph);
25023                 if (entities.other.length > 0 ||
25024                     entities.closedWay.length + entities.multipolygon.length < 2) {
25025                     return 'not_eligible';
25026                 }
25027                 if (!entities.multipolygon.every(function(r) { return r.isComplete(graph); })) {
25028                     return 'incomplete_relation';
25029                 }
25030
25031                 if (!entities.multipolygon.length) {
25032                     var sharedMultipolygons = [];
25033                     entities.closedWay.forEach(function(way, i) {
25034                         if (i === 0) {
25035                             sharedMultipolygons = graph.parentMultipolygons(way);
25036                         } else {
25037                             sharedMultipolygons = utilArrayIntersection(sharedMultipolygons, graph.parentMultipolygons(way));
25038                         }
25039                     });
25040                     sharedMultipolygons = sharedMultipolygons.filter(function(relation) {
25041                         return relation.members.length === entities.closedWay.length;
25042                     });
25043                     if (sharedMultipolygons.length) {
25044                         // don't create a new multipolygon if it'd be redundant
25045                         return 'not_eligible';
25046                     }
25047                 } else if (entities.closedWay.some(function(way) {
25048                         return utilArrayIntersection(graph.parentMultipolygons(way), entities.multipolygon).length;
25049                     })) {
25050                     // don't add a way to a multipolygon again if it's already a member
25051                     return 'not_eligible';
25052                 }
25053             };
25054
25055
25056             return action;
25057         }
25058
25059         // do not edit .js files directly - edit src/index.jst
25060
25061
25062
25063         var fastDeepEqual = function equal(a, b) {
25064           if (a === b) { return true; }
25065
25066           if (a && b && typeof a == 'object' && typeof b == 'object') {
25067             if (a.constructor !== b.constructor) { return false; }
25068
25069             var length, i, keys;
25070             if (Array.isArray(a)) {
25071               length = a.length;
25072               if (length != b.length) { return false; }
25073               for (i = length; i-- !== 0;)
25074                 { if (!equal(a[i], b[i])) { return false; } }
25075               return true;
25076             }
25077
25078
25079
25080             if (a.constructor === RegExp) { return a.source === b.source && a.flags === b.flags; }
25081             if (a.valueOf !== Object.prototype.valueOf) { return a.valueOf() === b.valueOf(); }
25082             if (a.toString !== Object.prototype.toString) { return a.toString() === b.toString(); }
25083
25084             keys = Object.keys(a);
25085             length = keys.length;
25086             if (length !== Object.keys(b).length) { return false; }
25087
25088             for (i = length; i-- !== 0;)
25089               { if (!Object.prototype.hasOwnProperty.call(b, keys[i])) { return false; } }
25090
25091             for (i = length; i-- !== 0;) {
25092               var key = keys[i];
25093
25094               if (!equal(a[key], b[key])) { return false; }
25095             }
25096
25097             return true;
25098           }
25099
25100           // true if both NaN, false otherwise
25101           return a!==a && b!==b;
25102         };
25103
25104         // Text diff algorithm following Hunt and McIlroy 1976.
25105         // J. W. Hunt and M. D. McIlroy, An algorithm for differential buffer
25106         // comparison, Bell Telephone Laboratories CSTR #41 (1976)
25107         // http://www.cs.dartmouth.edu/~doug/
25108         // https://en.wikipedia.org/wiki/Longest_common_subsequence_problem
25109         //
25110         // Expects two arrays, finds longest common sequence
25111         function LCS(buffer1, buffer2) {
25112
25113           var equivalenceClasses = {};
25114           for (var j = 0; j < buffer2.length; j++) {
25115             var item = buffer2[j];
25116             if (equivalenceClasses[item]) {
25117               equivalenceClasses[item].push(j);
25118             } else {
25119               equivalenceClasses[item] = [j];
25120             }
25121           }
25122
25123           var NULLRESULT = { buffer1index: -1, buffer2index: -1, chain: null };
25124           var candidates = [NULLRESULT];
25125
25126           for (var i = 0; i < buffer1.length; i++) {
25127             var item$1 = buffer1[i];
25128             var buffer2indices = equivalenceClasses[item$1] || [];
25129             var r = 0;
25130             var c = candidates[0];
25131
25132             for (var jx = 0; jx < buffer2indices.length; jx++) {
25133               var j$1 = buffer2indices[jx];
25134
25135               var s = (void 0);
25136               for (s = r; s < candidates.length; s++) {
25137                 if ((candidates[s].buffer2index < j$1) && ((s === candidates.length - 1) || (candidates[s + 1].buffer2index > j$1))) {
25138                   break;
25139                 }
25140               }
25141
25142               if (s < candidates.length) {
25143                 var newCandidate = { buffer1index: i, buffer2index: j$1, chain: candidates[s] };
25144                 if (r === candidates.length) {
25145                   candidates.push(c);
25146                 } else {
25147                   candidates[r] = c;
25148                 }
25149                 r = s + 1;
25150                 c = newCandidate;
25151                 if (r === candidates.length) {
25152                   break; // no point in examining further (j)s
25153                 }
25154               }
25155             }
25156
25157             candidates[r] = c;
25158           }
25159
25160           // At this point, we know the LCS: it's in the reverse of the
25161           // linked-list through .chain of candidates[candidates.length - 1].
25162
25163           return candidates[candidates.length - 1];
25164         }
25165
25166
25167         // We apply the LCS to give a simple representation of the
25168         // offsets and lengths of mismatched chunks in the input
25169         // buffers. This is used by diff3MergeRegions.
25170         function diffIndices(buffer1, buffer2) {
25171           var lcs = LCS(buffer1, buffer2);
25172           var result = [];
25173           var tail1 = buffer1.length;
25174           var tail2 = buffer2.length;
25175
25176           for (var candidate = lcs; candidate !== null; candidate = candidate.chain) {
25177             var mismatchLength1 = tail1 - candidate.buffer1index - 1;
25178             var mismatchLength2 = tail2 - candidate.buffer2index - 1;
25179             tail1 = candidate.buffer1index;
25180             tail2 = candidate.buffer2index;
25181
25182             if (mismatchLength1 || mismatchLength2) {
25183               result.push({
25184                 buffer1: [tail1 + 1, mismatchLength1],
25185                 buffer1Content: buffer1.slice(tail1 + 1, tail1 + 1 + mismatchLength1),
25186                 buffer2: [tail2 + 1, mismatchLength2],
25187                 buffer2Content: buffer2.slice(tail2 + 1, tail2 + 1 + mismatchLength2)
25188               });
25189             }
25190           }
25191
25192           result.reverse();
25193           return result;
25194         }
25195
25196
25197         // Given three buffers, A, O, and B, where both A and B are
25198         // independently derived from O, returns a fairly complicated
25199         // internal representation of merge decisions it's taken. The
25200         // interested reader may wish to consult
25201         //
25202         // Sanjeev Khanna, Keshav Kunal, and Benjamin C. Pierce.
25203         // 'A Formal Investigation of ' In Arvind and Prasad,
25204         // editors, Foundations of Software Technology and Theoretical
25205         // Computer Science (FSTTCS), December 2007.
25206         //
25207         // (http://www.cis.upenn.edu/~bcpierce/papers/diff3-short.pdf)
25208         //
25209         function diff3MergeRegions(a, o, b) {
25210
25211           // "hunks" are array subsets where `a` or `b` are different from `o`
25212           // https://www.gnu.org/software/diffutils/manual/html_node/diff3-Hunks.html
25213           var hunks = [];
25214           function addHunk(h, ab) {
25215             hunks.push({
25216               ab: ab,
25217               oStart: h.buffer1[0],
25218               oLength: h.buffer1[1],   // length of o to remove
25219               abStart: h.buffer2[0],
25220               abLength: h.buffer2[1]   // length of a/b to insert
25221               // abContent: (ab === 'a' ? a : b).slice(h.buffer2[0], h.buffer2[0] + h.buffer2[1])
25222             });
25223           }
25224
25225           diffIndices(o, a).forEach(function (item) { return addHunk(item, 'a'); });
25226           diffIndices(o, b).forEach(function (item) { return addHunk(item, 'b'); });
25227           hunks.sort(function (x,y) { return x.oStart - y.oStart; });
25228
25229           var results = [];
25230           var currOffset = 0;
25231
25232           function advanceTo(endOffset) {
25233             if (endOffset > currOffset) {
25234               results.push({
25235                 stable: true,
25236                 buffer: 'o',
25237                 bufferStart: currOffset,
25238                 bufferLength: endOffset - currOffset,
25239                 bufferContent: o.slice(currOffset, endOffset)
25240               });
25241               currOffset = endOffset;
25242             }
25243           }
25244
25245           while (hunks.length) {
25246             var hunk = hunks.shift();
25247             var regionStart = hunk.oStart;
25248             var regionEnd = hunk.oStart + hunk.oLength;
25249             var regionHunks = [hunk];
25250             advanceTo(regionStart);
25251
25252             // Try to pull next overlapping hunk into this region
25253             while (hunks.length) {
25254               var nextHunk = hunks[0];
25255               var nextHunkStart = nextHunk.oStart;
25256               if (nextHunkStart > regionEnd) { break; }   // no overlap
25257
25258               regionEnd = Math.max(regionEnd, nextHunkStart + nextHunk.oLength);
25259               regionHunks.push(hunks.shift());
25260             }
25261
25262             if (regionHunks.length === 1) {
25263               // Only one hunk touches this region, meaning that there is no conflict here.
25264               // Either `a` or `b` is inserting into a region of `o` unchanged by the other.
25265               if (hunk.abLength > 0) {
25266                 var buffer = (hunk.ab === 'a' ? a : b);
25267                 results.push({
25268                   stable: true,
25269                   buffer: hunk.ab,
25270                   bufferStart: hunk.abStart,
25271                   bufferLength: hunk.abLength,
25272                   bufferContent: buffer.slice(hunk.abStart, hunk.abStart + hunk.abLength)
25273                 });
25274               }
25275             } else {
25276               // A true a/b conflict. Determine the bounds involved from `a`, `o`, and `b`.
25277               // Effectively merge all the `a` hunks into one giant hunk, then do the
25278               // same for the `b` hunks; then, correct for skew in the regions of `o`
25279               // that each side changed, and report appropriate spans for the three sides.
25280               var bounds = {
25281                 a: [a.length, -1, o.length, -1],
25282                 b: [b.length, -1, o.length, -1]
25283               };
25284               while (regionHunks.length) {
25285                 hunk = regionHunks.shift();
25286                 var oStart = hunk.oStart;
25287                 var oEnd = oStart + hunk.oLength;
25288                 var abStart = hunk.abStart;
25289                 var abEnd = abStart + hunk.abLength;
25290                 var b$1 = bounds[hunk.ab];
25291                 b$1[0] = Math.min(abStart, b$1[0]);
25292                 b$1[1] = Math.max(abEnd, b$1[1]);
25293                 b$1[2] = Math.min(oStart, b$1[2]);
25294                 b$1[3] = Math.max(oEnd, b$1[3]);
25295               }
25296
25297               var aStart = bounds.a[0] + (regionStart - bounds.a[2]);
25298               var aEnd = bounds.a[1] + (regionEnd - bounds.a[3]);
25299               var bStart = bounds.b[0] + (regionStart - bounds.b[2]);
25300               var bEnd = bounds.b[1] + (regionEnd - bounds.b[3]);
25301
25302               var result = {
25303                 stable: false,
25304                 aStart: aStart,
25305                 aLength: aEnd - aStart,
25306                 aContent: a.slice(aStart, aEnd),
25307                 oStart: regionStart,
25308                 oLength: regionEnd - regionStart,
25309                 oContent: o.slice(regionStart, regionEnd),
25310                 bStart: bStart,
25311                 bLength: bEnd - bStart,
25312                 bContent: b.slice(bStart, bEnd)
25313               };
25314               results.push(result);
25315             }
25316             currOffset = regionEnd;
25317           }
25318
25319           advanceTo(o.length);
25320
25321           return results;
25322         }
25323
25324
25325         // Applies the output of diff3MergeRegions to actually
25326         // construct the merged buffer; the returned result alternates
25327         // between 'ok' and 'conflict' blocks.
25328         // A "false conflict" is where `a` and `b` both change the same from `o`
25329         function diff3Merge(a, o, b, options) {
25330           var defaults = {
25331             excludeFalseConflicts: true,
25332             stringSeparator: /\s+/
25333           };
25334           options = Object.assign(defaults, options);
25335
25336           var aString = (typeof a === 'string');
25337           var oString = (typeof o === 'string');
25338           var bString = (typeof b === 'string');
25339
25340           if (aString) { a = a.split(options.stringSeparator); }
25341           if (oString) { o = o.split(options.stringSeparator); }
25342           if (bString) { b = b.split(options.stringSeparator); }
25343
25344           var results = [];
25345           var regions = diff3MergeRegions(a, o, b);
25346
25347           var okBuffer = [];
25348           function flushOk() {
25349             if (okBuffer.length) {
25350               results.push({ ok: okBuffer });
25351             }
25352             okBuffer = [];
25353           }
25354
25355           function isFalseConflict(a, b) {
25356             if (a.length !== b.length) { return false; }
25357             for (var i = 0; i < a.length; i++) {
25358               if (a[i] !== b[i]) { return false; }
25359             }
25360             return true;
25361           }
25362
25363           regions.forEach(function (region) {
25364             if (region.stable) {
25365               okBuffer.push.apply(okBuffer, region.bufferContent);
25366             } else {
25367               if (options.excludeFalseConflicts && isFalseConflict(region.aContent, region.bContent)) {
25368                 okBuffer.push.apply(okBuffer, region.aContent);
25369               } else {
25370                 flushOk();
25371                 results.push({
25372                   conflict: {
25373                     a: region.aContent,
25374                     aIndex: region.aStart,
25375                     o: region.oContent,
25376                     oIndex: region.oStart,
25377                     b: region.bContent,
25378                     bIndex: region.bStart
25379                   }
25380                 });
25381               }
25382             }
25383           });
25384
25385           flushOk();
25386           return results;
25387         }
25388
25389         function actionMergeRemoteChanges(id, localGraph, remoteGraph, discardTags, formatUser) {
25390             discardTags = discardTags || {};
25391             var _option = 'safe';  // 'safe', 'force_local', 'force_remote'
25392             var _conflicts = [];
25393
25394
25395             function user(d) {
25396                 return (typeof formatUser === 'function') ? formatUser(d) : d;
25397             }
25398
25399
25400             function mergeLocation(remote, target) {
25401                 function pointEqual(a, b) {
25402                     var epsilon = 1e-6;
25403                     return (Math.abs(a[0] - b[0]) < epsilon) && (Math.abs(a[1] - b[1]) < epsilon);
25404                 }
25405
25406                 if (_option === 'force_local' || pointEqual(target.loc, remote.loc)) {
25407                     return target;
25408                 }
25409                 if (_option === 'force_remote') {
25410                     return target.update({loc: remote.loc});
25411                 }
25412
25413                 _conflicts.push(_t('merge_remote_changes.conflict.location', { user: user(remote.user) }));
25414                 return target;
25415             }
25416
25417
25418             function mergeNodes(base, remote, target) {
25419                 if (_option === 'force_local' || fastDeepEqual(target.nodes, remote.nodes)) {
25420                     return target;
25421                 }
25422                 if (_option === 'force_remote') {
25423                     return target.update({nodes: remote.nodes});
25424                 }
25425
25426                 var ccount = _conflicts.length;
25427                 var o = base.nodes || [];
25428                 var a = target.nodes || [];
25429                 var b = remote.nodes || [];
25430                 var nodes = [];
25431                 var hunks = diff3Merge(a, o, b, { excludeFalseConflicts: true });
25432
25433                 for (var i = 0; i < hunks.length; i++) {
25434                     var hunk = hunks[i];
25435                     if (hunk.ok) {
25436                         nodes.push.apply(nodes, hunk.ok);
25437                     } else {
25438                         // for all conflicts, we can assume c.a !== c.b
25439                         // because `diff3Merge` called with `true` option to exclude false conflicts..
25440                         var c = hunk.conflict;
25441                         if (fastDeepEqual(c.o, c.a)) {  // only changed remotely
25442                             nodes.push.apply(nodes, c.b);
25443                         } else if (fastDeepEqual(c.o, c.b)) {  // only changed locally
25444                             nodes.push.apply(nodes, c.a);
25445                         } else {       // changed both locally and remotely
25446                             _conflicts.push(_t('merge_remote_changes.conflict.nodelist', { user: user(remote.user) }));
25447                             break;
25448                         }
25449                     }
25450                 }
25451
25452                 return (_conflicts.length === ccount) ? target.update({nodes: nodes}) : target;
25453             }
25454
25455
25456             function mergeChildren(targetWay, children, updates, graph) {
25457                 function isUsed(node, targetWay) {
25458                     var hasInterestingParent = graph.parentWays(node)
25459                         .some(function(way) { return way.id !== targetWay.id; });
25460
25461                     return node.hasInterestingTags() ||
25462                         hasInterestingParent ||
25463                         graph.parentRelations(node).length > 0;
25464                 }
25465
25466                 var ccount = _conflicts.length;
25467
25468                 for (var i = 0; i < children.length; i++) {
25469                     var id = children[i];
25470                     var node = graph.hasEntity(id);
25471
25472                     // remove unused childNodes..
25473                     if (targetWay.nodes.indexOf(id) === -1) {
25474                         if (node && !isUsed(node, targetWay)) {
25475                             updates.removeIds.push(id);
25476                         }
25477                         continue;
25478                     }
25479
25480                     // restore used childNodes..
25481                     var local = localGraph.hasEntity(id);
25482                     var remote = remoteGraph.hasEntity(id);
25483                     var target;
25484
25485                     if (_option === 'force_remote' && remote && remote.visible) {
25486                         updates.replacements.push(remote);
25487
25488                     } else if (_option === 'force_local' && local) {
25489                         target = osmEntity(local);
25490                         if (remote) {
25491                             target = target.update({ version: remote.version });
25492                         }
25493                         updates.replacements.push(target);
25494
25495                     } else if (_option === 'safe' && local && remote && local.version !== remote.version) {
25496                         target = osmEntity(local, { version: remote.version });
25497                         if (remote.visible) {
25498                             target = mergeLocation(remote, target);
25499                         } else {
25500                             _conflicts.push(_t('merge_remote_changes.conflict.deleted', { user: user(remote.user) }));
25501                         }
25502
25503                         if (_conflicts.length !== ccount) { break; }
25504                         updates.replacements.push(target);
25505                     }
25506                 }
25507
25508                 return targetWay;
25509             }
25510
25511
25512             function updateChildren(updates, graph) {
25513                 for (var i = 0; i < updates.replacements.length; i++) {
25514                     graph = graph.replace(updates.replacements[i]);
25515                 }
25516                 if (updates.removeIds.length) {
25517                     graph = actionDeleteMultiple(updates.removeIds)(graph);
25518                 }
25519                 return graph;
25520             }
25521
25522
25523             function mergeMembers(remote, target) {
25524                 if (_option === 'force_local' || fastDeepEqual(target.members, remote.members)) {
25525                     return target;
25526                 }
25527                 if (_option === 'force_remote') {
25528                     return target.update({members: remote.members});
25529                 }
25530
25531                 _conflicts.push(_t('merge_remote_changes.conflict.memberlist', { user: user(remote.user) }));
25532                 return target;
25533             }
25534
25535
25536             function mergeTags(base, remote, target) {
25537                 if (_option === 'force_local' || fastDeepEqual(target.tags, remote.tags)) {
25538                     return target;
25539                 }
25540                 if (_option === 'force_remote') {
25541                     return target.update({tags: remote.tags});
25542                 }
25543
25544                 var ccount = _conflicts.length;
25545                 var o = base.tags || {};
25546                 var a = target.tags || {};
25547                 var b = remote.tags || {};
25548                 var keys = utilArrayUnion(utilArrayUnion(Object.keys(o), Object.keys(a)), Object.keys(b))
25549                     .filter(function(k) { return !discardTags[k]; });
25550                 var tags = Object.assign({}, a);   // shallow copy
25551                 var changed = false;
25552
25553                 for (var i = 0; i < keys.length; i++) {
25554                     var k = keys[i];
25555
25556                     if (o[k] !== b[k] && a[k] !== b[k]) {    // changed remotely..
25557                         if (o[k] !== a[k]) {      // changed locally..
25558                             _conflicts.push(_t('merge_remote_changes.conflict.tags',
25559                                 { tag: k, local: a[k], remote: b[k], user: user(remote.user) }));
25560
25561                         } else {                  // unchanged locally, accept remote change..
25562                             if (b.hasOwnProperty(k)) {
25563                                 tags[k] = b[k];
25564                             } else {
25565                                 delete tags[k];
25566                             }
25567                             changed = true;
25568                         }
25569                     }
25570                 }
25571
25572                 return (changed && _conflicts.length === ccount) ? target.update({tags: tags}) : target;
25573             }
25574
25575
25576             //  `graph.base()` is the common ancestor of the two graphs.
25577             //  `localGraph` contains user's edits up to saving
25578             //  `remoteGraph` contains remote edits to modified nodes
25579             //  `graph` must be a descendent of `localGraph` and may include
25580             //      some conflict resolution actions performed on it.
25581             //
25582             //                  --- ... --- `localGraph` -- ... -- `graph`
25583             //                 /
25584             //  `graph.base()` --- ... --- `remoteGraph`
25585             //
25586             var action = function(graph) {
25587                 var updates = { replacements: [], removeIds: [] };
25588                 var base = graph.base().entities[id];
25589                 var local = localGraph.entity(id);
25590                 var remote = remoteGraph.entity(id);
25591                 var target = osmEntity(local, { version: remote.version });
25592
25593                 // delete/undelete
25594                 if (!remote.visible) {
25595                     if (_option === 'force_remote') {
25596                         return actionDeleteMultiple([id])(graph);
25597
25598                     } else if (_option === 'force_local') {
25599                         if (target.type === 'way') {
25600                             target = mergeChildren(target, utilArrayUniq(local.nodes), updates, graph);
25601                             graph = updateChildren(updates, graph);
25602                         }
25603                         return graph.replace(target);
25604
25605                     } else {
25606                         _conflicts.push(_t('merge_remote_changes.conflict.deleted', { user: user(remote.user) }));
25607                         return graph;  // do nothing
25608                     }
25609                 }
25610
25611                 // merge
25612                 if (target.type === 'node') {
25613                     target = mergeLocation(remote, target);
25614
25615                 } else if (target.type === 'way') {
25616                     // pull in any child nodes that may not be present locally..
25617                     graph.rebase(remoteGraph.childNodes(remote), [graph], false);
25618                     target = mergeNodes(base, remote, target);
25619                     target = mergeChildren(target, utilArrayUnion(local.nodes, remote.nodes), updates, graph);
25620
25621                 } else if (target.type === 'relation') {
25622                     target = mergeMembers(remote, target);
25623                 }
25624
25625                 target = mergeTags(base, remote, target);
25626
25627                 if (!_conflicts.length) {
25628                     graph = updateChildren(updates, graph).replace(target);
25629                 }
25630
25631                 return graph;
25632             };
25633
25634
25635             action.withOption = function(opt) {
25636                 _option = opt;
25637                 return action;
25638             };
25639
25640
25641             action.conflicts = function() {
25642                 return _conflicts;
25643             };
25644
25645
25646             return action;
25647         }
25648
25649         // https://github.com/openstreetmap/josm/blob/mirror/src/org/openstreetmap/josm/command/MoveCommand.java
25650         // https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/MoveNodeAction.as
25651         function actionMove(moveIDs, tryDelta, projection, cache) {
25652             var _delta = tryDelta;
25653
25654             function setupCache(graph) {
25655                 function canMove(nodeID) {
25656                     // Allow movement of any node that is in the selectedIDs list..
25657                     if (moveIDs.indexOf(nodeID) !== -1) { return true; }
25658
25659                     // Allow movement of a vertex where 2 ways meet..
25660                     var parents = graph.parentWays(graph.entity(nodeID));
25661                     if (parents.length < 3) { return true; }
25662
25663                     // Restrict movement of a vertex where >2 ways meet, unless all parentWays are moving too..
25664                     var parentsMoving = parents.every(function(way) { return cache.moving[way.id]; });
25665                     if (!parentsMoving) { delete cache.moving[nodeID]; }
25666
25667                     return parentsMoving;
25668                 }
25669
25670                 function cacheEntities(ids) {
25671                     for (var i = 0; i < ids.length; i++) {
25672                         var id = ids[i];
25673                         if (cache.moving[id]) { continue; }
25674                         cache.moving[id] = true;
25675
25676                         var entity = graph.hasEntity(id);
25677                         if (!entity) { continue; }
25678
25679                         if (entity.type === 'node') {
25680                             cache.nodes.push(id);
25681                             cache.startLoc[id] = entity.loc;
25682                         } else if (entity.type === 'way') {
25683                             cache.ways.push(id);
25684                             cacheEntities(entity.nodes);
25685                         } else {
25686                             cacheEntities(entity.members.map(function(member) {
25687                                 return member.id;
25688                             }));
25689                         }
25690                     }
25691                 }
25692
25693                 function cacheIntersections(ids) {
25694                     function isEndpoint(way, id) {
25695                         return !way.isClosed() && !!way.affix(id);
25696                     }
25697
25698                     for (var i = 0; i < ids.length; i++) {
25699                         var id = ids[i];
25700
25701                         // consider only intersections with 1 moved and 1 unmoved way.
25702                         var childNodes = graph.childNodes(graph.entity(id));
25703                         for (var j = 0; j < childNodes.length; j++) {
25704                             var node = childNodes[j];
25705                             var parents = graph.parentWays(node);
25706                             if (parents.length !== 2) { continue; }
25707
25708                             var moved = graph.entity(id);
25709                             var unmoved = null;
25710                             for (var k = 0; k < parents.length; k++) {
25711                                 var way = parents[k];
25712                                 if (!cache.moving[way.id]) {
25713                                     unmoved = way;
25714                                     break;
25715                                 }
25716                             }
25717                             if (!unmoved) { continue; }
25718
25719                             // exclude ways that are overly connected..
25720                             if (utilArrayIntersection(moved.nodes, unmoved.nodes).length > 2) { continue; }
25721                             if (moved.isArea() || unmoved.isArea()) { continue; }
25722
25723                             cache.intersections.push({
25724                                 nodeId: node.id,
25725                                 movedId: moved.id,
25726                                 unmovedId: unmoved.id,
25727                                 movedIsEP: isEndpoint(moved, node.id),
25728                                 unmovedIsEP: isEndpoint(unmoved, node.id)
25729                             });
25730                         }
25731                     }
25732                 }
25733
25734
25735                 if (!cache) {
25736                     cache = {};
25737                 }
25738                 if (!cache.ok) {
25739                     cache.moving = {};
25740                     cache.intersections = [];
25741                     cache.replacedVertex = {};
25742                     cache.startLoc = {};
25743                     cache.nodes = [];
25744                     cache.ways = [];
25745
25746                     cacheEntities(moveIDs);
25747                     cacheIntersections(cache.ways);
25748                     cache.nodes = cache.nodes.filter(canMove);
25749
25750                     cache.ok = true;
25751                 }
25752             }
25753
25754
25755             // Place a vertex where the moved vertex used to be, to preserve way shape..
25756             //
25757             //  Start:
25758             //      b ---- e
25759             //     / \
25760             //    /   \
25761             //   /     \
25762             //  a       c
25763             //
25764             //      *               node '*' added to preserve shape
25765             //     / \
25766             //    /   b ---- e      way `b,e` moved here:
25767             //   /     \
25768             //  a       c
25769             //
25770             //
25771             function replaceMovedVertex(nodeId, wayId, graph, delta) {
25772                 var way = graph.entity(wayId);
25773                 var moved = graph.entity(nodeId);
25774                 var movedIndex = way.nodes.indexOf(nodeId);
25775                 var len, prevIndex, nextIndex;
25776
25777                 if (way.isClosed()) {
25778                     len = way.nodes.length - 1;
25779                     prevIndex = (movedIndex + len - 1) % len;
25780                     nextIndex = (movedIndex + len + 1) % len;
25781                 } else {
25782                     len = way.nodes.length;
25783                     prevIndex = movedIndex - 1;
25784                     nextIndex = movedIndex + 1;
25785                 }
25786
25787                 var prev = graph.hasEntity(way.nodes[prevIndex]);
25788                 var next = graph.hasEntity(way.nodes[nextIndex]);
25789
25790                 // Don't add orig vertex at endpoint..
25791                 if (!prev || !next) { return graph; }
25792
25793                 var key = wayId + '_' + nodeId;
25794                 var orig = cache.replacedVertex[key];
25795                 if (!orig) {
25796                     orig = osmNode();
25797                     cache.replacedVertex[key] = orig;
25798                     cache.startLoc[orig.id] = cache.startLoc[nodeId];
25799                 }
25800
25801                 var start, end;
25802                 if (delta) {
25803                     start = projection(cache.startLoc[nodeId]);
25804                     end = projection.invert(geoVecAdd(start, delta));
25805                 } else {
25806                     end = cache.startLoc[nodeId];
25807                 }
25808                 orig = orig.move(end);
25809
25810                 var angle = Math.abs(geoAngle(orig, prev, projection) -
25811                         geoAngle(orig, next, projection)) * 180 / Math.PI;
25812
25813                 // Don't add orig vertex if it would just make a straight line..
25814                 if (angle > 175 && angle < 185) { return graph; }
25815
25816                 // moving forward or backward along way?
25817                 var p1 = [prev.loc, orig.loc, moved.loc, next.loc].map(projection);
25818                 var p2 = [prev.loc, moved.loc, orig.loc, next.loc].map(projection);
25819                 var d1 = geoPathLength(p1);
25820                 var d2 = geoPathLength(p2);
25821                 var insertAt = (d1 <= d2) ? movedIndex : nextIndex;
25822
25823                 // moving around closed loop?
25824                 if (way.isClosed() && insertAt === 0) { insertAt = len; }
25825
25826                 way = way.addNode(orig.id, insertAt);
25827                 return graph.replace(orig).replace(way);
25828             }
25829
25830
25831             // Remove duplicate vertex that might have been added by
25832             // replaceMovedVertex.  This is done after the unzorro checks.
25833             function removeDuplicateVertices(wayId, graph) {
25834                 var way = graph.entity(wayId);
25835                 var epsilon = 1e-6;
25836                 var prev, curr;
25837
25838                 function isInteresting(node, graph) {
25839                     return graph.parentWays(node).length > 1 ||
25840                         graph.parentRelations(node).length ||
25841                         node.hasInterestingTags();
25842                 }
25843
25844                 for (var i = 0; i < way.nodes.length; i++) {
25845                     curr = graph.entity(way.nodes[i]);
25846
25847                     if (prev && curr && geoVecEqual(prev.loc, curr.loc, epsilon)) {
25848                         if (!isInteresting(prev, graph)) {
25849                             way = way.removeNode(prev.id);
25850                             graph = graph.replace(way).remove(prev);
25851                         } else if (!isInteresting(curr, graph)) {
25852                             way = way.removeNode(curr.id);
25853                             graph = graph.replace(way).remove(curr);
25854                         }
25855                     }
25856
25857                     prev = curr;
25858                 }
25859
25860                 return graph;
25861             }
25862
25863
25864             // Reorder nodes around intersections that have moved..
25865             //
25866             //  Start:                way1.nodes: b,e         (moving)
25867             //  a - b - c ----- d     way2.nodes: a,b,c,d     (static)
25868             //      |                 vertex: b
25869             //      e                 isEP1: true,  isEP2, false
25870             //
25871             //  way1 `b,e` moved here:
25872             //  a ----- c = b - d
25873             //              |
25874             //              e
25875             //
25876             //  reorder nodes         way1.nodes: b,e
25877             //  a ----- c - b - d     way2.nodes: a,c,b,d
25878             //              |
25879             //              e
25880             //
25881             function unZorroIntersection(intersection, graph) {
25882                 var vertex = graph.entity(intersection.nodeId);
25883                 var way1 = graph.entity(intersection.movedId);
25884                 var way2 = graph.entity(intersection.unmovedId);
25885                 var isEP1 = intersection.movedIsEP;
25886                 var isEP2 = intersection.unmovedIsEP;
25887
25888                 // don't move the vertex if it is the endpoint of both ways.
25889                 if (isEP1 && isEP2) { return graph; }
25890
25891                 var nodes1 = graph.childNodes(way1).filter(function(n) { return n !== vertex; });
25892                 var nodes2 = graph.childNodes(way2).filter(function(n) { return n !== vertex; });
25893
25894                 if (way1.isClosed() && way1.first() === vertex.id) { nodes1.push(nodes1[0]); }
25895                 if (way2.isClosed() && way2.first() === vertex.id) { nodes2.push(nodes2[0]); }
25896
25897                 var edge1 = !isEP1 && geoChooseEdge(nodes1, projection(vertex.loc), projection);
25898                 var edge2 = !isEP2 && geoChooseEdge(nodes2, projection(vertex.loc), projection);
25899                 var loc;
25900
25901                 // snap vertex to nearest edge (or some point between them)..
25902                 if (!isEP1 && !isEP2) {
25903                     var epsilon = 1e-6, maxIter = 10;
25904                     for (var i = 0; i < maxIter; i++) {
25905                         loc = geoVecInterp(edge1.loc, edge2.loc, 0.5);
25906                         edge1 = geoChooseEdge(nodes1, projection(loc), projection);
25907                         edge2 = geoChooseEdge(nodes2, projection(loc), projection);
25908                         if (Math.abs(edge1.distance - edge2.distance) < epsilon) { break; }
25909                     }
25910                 } else if (!isEP1) {
25911                     loc = edge1.loc;
25912                 } else {
25913                     loc = edge2.loc;
25914                 }
25915
25916                 graph = graph.replace(vertex.move(loc));
25917
25918                 // if zorro happened, reorder nodes..
25919                 if (!isEP1 && edge1.index !== way1.nodes.indexOf(vertex.id)) {
25920                     way1 = way1.removeNode(vertex.id).addNode(vertex.id, edge1.index);
25921                     graph = graph.replace(way1);
25922                 }
25923                 if (!isEP2 && edge2.index !== way2.nodes.indexOf(vertex.id)) {
25924                     way2 = way2.removeNode(vertex.id).addNode(vertex.id, edge2.index);
25925                     graph = graph.replace(way2);
25926                 }
25927
25928                 return graph;
25929             }
25930
25931
25932             function cleanupIntersections(graph) {
25933                 for (var i = 0; i < cache.intersections.length; i++) {
25934                     var obj = cache.intersections[i];
25935                     graph = replaceMovedVertex(obj.nodeId, obj.movedId, graph, _delta);
25936                     graph = replaceMovedVertex(obj.nodeId, obj.unmovedId, graph, null);
25937                     graph = unZorroIntersection(obj, graph);
25938                     graph = removeDuplicateVertices(obj.movedId, graph);
25939                     graph = removeDuplicateVertices(obj.unmovedId, graph);
25940                 }
25941
25942                 return graph;
25943             }
25944
25945
25946             // check if moving way endpoint can cross an unmoved way, if so limit delta..
25947             function limitDelta(graph) {
25948                 function moveNode(loc) {
25949                     return geoVecAdd(projection(loc), _delta);
25950                 }
25951
25952                 for (var i = 0; i < cache.intersections.length; i++) {
25953                     var obj = cache.intersections[i];
25954
25955                     // Don't limit movement if this is vertex joins 2 endpoints..
25956                     if (obj.movedIsEP && obj.unmovedIsEP) { continue; }
25957                     // Don't limit movement if this vertex is not an endpoint anyway..
25958                     if (!obj.movedIsEP) { continue; }
25959
25960                     var node = graph.entity(obj.nodeId);
25961                     var start = projection(node.loc);
25962                     var end = geoVecAdd(start, _delta);
25963                     var movedNodes = graph.childNodes(graph.entity(obj.movedId));
25964                     var movedPath = movedNodes.map(function(n) { return moveNode(n.loc); });
25965                     var unmovedNodes = graph.childNodes(graph.entity(obj.unmovedId));
25966                     var unmovedPath = unmovedNodes.map(function(n) { return projection(n.loc); });
25967                     var hits = geoPathIntersections(movedPath, unmovedPath);
25968
25969                     for (var j = 0; i < hits.length; i++) {
25970                         if (geoVecEqual(hits[j], end)) { continue; }
25971                         var edge = geoChooseEdge(unmovedNodes, end, projection);
25972                         _delta = geoVecSubtract(projection(edge.loc), start);
25973                     }
25974                 }
25975             }
25976
25977
25978             var action = function(graph) {
25979                 if (_delta[0] === 0 && _delta[1] === 0) { return graph; }
25980
25981                 setupCache(graph);
25982
25983                 if (cache.intersections.length) {
25984                     limitDelta(graph);
25985                 }
25986
25987                 for (var i = 0; i < cache.nodes.length; i++) {
25988                     var node = graph.entity(cache.nodes[i]);
25989                     var start = projection(node.loc);
25990                     var end = geoVecAdd(start, _delta);
25991                     graph = graph.replace(node.move(projection.invert(end)));
25992                 }
25993
25994                 if (cache.intersections.length) {
25995                     graph = cleanupIntersections(graph);
25996                 }
25997
25998                 return graph;
25999             };
26000
26001
26002             action.delta = function() {
26003                 return _delta;
26004             };
26005
26006
26007             return action;
26008         }
26009
26010         function actionMoveMember(relationId, fromIndex, toIndex) {
26011             return function(graph) {
26012                 return graph.replace(graph.entity(relationId).moveMember(fromIndex, toIndex));
26013             };
26014         }
26015
26016         function actionMoveNode(nodeID, toLoc) {
26017
26018             var action = function(graph, t) {
26019                 if (t === null || !isFinite(t)) { t = 1; }
26020                 t = Math.min(Math.max(+t, 0), 1);
26021
26022                 var node = graph.entity(nodeID);
26023                 return graph.replace(
26024                     node.move(geoVecInterp(node.loc, toLoc, t))
26025                 );
26026             };
26027
26028             action.transitionable = true;
26029
26030             return action;
26031         }
26032
26033         function actionNoop() {
26034             return function(graph) {
26035                 return graph;
26036             };
26037         }
26038
26039         function actionOrthogonalize(wayID, projection, vertexID, degThresh, ep) {
26040             var epsilon = ep || 1e-4;
26041             var threshold = degThresh || 13;  // degrees within right or straight to alter
26042
26043             // We test normalized dot products so we can compare as cos(angle)
26044             var lowerThreshold = Math.cos((90 - threshold) * Math.PI / 180);
26045             var upperThreshold = Math.cos(threshold * Math.PI / 180);
26046
26047
26048             var action = function(graph, t) {
26049                 if (t === null || !isFinite(t)) { t = 1; }
26050                 t = Math.min(Math.max(+t, 0), 1);
26051
26052                 var way = graph.entity(wayID);
26053                 way = way.removeNode('');   // sanity check - remove any consecutive duplicates
26054
26055                 if (way.tags.nonsquare) {
26056                     var tags = Object.assign({}, way.tags);
26057                     // since we're squaring, remove indication that this is physically unsquare
26058                     delete tags.nonsquare;
26059                     way = way.update({tags: tags});
26060                 }
26061
26062                 graph = graph.replace(way);
26063
26064                 var isClosed = way.isClosed();
26065                 var nodes = graph.childNodes(way).slice();  // shallow copy
26066                 if (isClosed) { nodes.pop(); }
26067
26068                 if (vertexID !== undefined) {
26069                     nodes = nodeSubset(nodes, vertexID, isClosed);
26070                     if (nodes.length !== 3) { return graph; }
26071                 }
26072
26073                 // note: all geometry functions here use the unclosed node/point/coord list
26074
26075                 var nodeCount = {};
26076                 var points = [];
26077                 var corner = { i: 0, dotp: 1 };
26078                 var node, point, loc, score, motions, i, j;
26079
26080                 for (i = 0; i < nodes.length; i++) {
26081                     node = nodes[i];
26082                     nodeCount[node.id] = (nodeCount[node.id] || 0) + 1;
26083                     points.push({ id: node.id, coord: projection(node.loc) });
26084                 }
26085
26086
26087                 if (points.length === 3) {   // move only one vertex for right triangle
26088                     for (i = 0; i < 1000; i++) {
26089                         motions = points.map(calcMotion);
26090
26091                         points[corner.i].coord = geoVecAdd(points[corner.i].coord, motions[corner.i]);
26092                         score = corner.dotp;
26093                         if (score < epsilon) {
26094                             break;
26095                         }
26096                     }
26097
26098                     node = graph.entity(nodes[corner.i].id);
26099                     loc = projection.invert(points[corner.i].coord);
26100                     graph = graph.replace(node.move(geoVecInterp(node.loc, loc, t)));
26101
26102                 } else {
26103                     var straights = [];
26104                     var simplified = [];
26105
26106                     // Remove points from nearly straight sections..
26107                     // This produces a simplified shape to orthogonalize
26108                     for (i = 0; i < points.length; i++) {
26109                         point = points[i];
26110                         var dotp = 0;
26111                         if (isClosed || (i > 0 && i < points.length - 1)) {
26112                             var a = points[(i - 1 + points.length) % points.length];
26113                             var b = points[(i + 1) % points.length];
26114                             dotp = Math.abs(geoOrthoNormalizedDotProduct(a.coord, b.coord, point.coord));
26115                         }
26116
26117                         if (dotp > upperThreshold) {
26118                             straights.push(point);
26119                         } else {
26120                             simplified.push(point);
26121                         }
26122                     }
26123
26124                     // Orthogonalize the simplified shape
26125                     var bestPoints = clonePoints(simplified);
26126                     var originalPoints = clonePoints(simplified);
26127
26128                     score = Infinity;
26129                     for (i = 0; i < 1000; i++) {
26130                         motions = simplified.map(calcMotion);
26131
26132                         for (j = 0; j < motions.length; j++) {
26133                             simplified[j].coord = geoVecAdd(simplified[j].coord, motions[j]);
26134                         }
26135                         var newScore = geoOrthoCalcScore(simplified, isClosed, epsilon, threshold);
26136                         if (newScore < score) {
26137                             bestPoints = clonePoints(simplified);
26138                             score = newScore;
26139                         }
26140                         if (score < epsilon) {
26141                             break;
26142                         }
26143                     }
26144
26145                     var bestCoords = bestPoints.map(function(p) { return p.coord; });
26146                     if (isClosed) { bestCoords.push(bestCoords[0]); }
26147
26148                     // move the nodes that should move
26149                     for (i = 0; i < bestPoints.length; i++) {
26150                         point = bestPoints[i];
26151                         if (!geoVecEqual(originalPoints[i].coord, point.coord)) {
26152                             node = graph.entity(point.id);
26153                             loc = projection.invert(point.coord);
26154                             graph = graph.replace(node.move(geoVecInterp(node.loc, loc, t)));
26155                         }
26156                     }
26157
26158                     // move the nodes along straight segments
26159                     for (i = 0; i < straights.length; i++) {
26160                         point = straights[i];
26161                         if (nodeCount[point.id] > 1) { continue; }   // skip self-intersections
26162
26163                         node = graph.entity(point.id);
26164
26165                         if (t === 1 &&
26166                             graph.parentWays(node).length === 1 &&
26167                             graph.parentRelations(node).length === 0 &&
26168                             !node.hasInterestingTags()
26169                         ) {
26170                             // remove uninteresting points..
26171                             graph = actionDeleteNode(node.id)(graph);
26172
26173                         } else {
26174                             // move interesting points to the nearest edge..
26175                             var choice = geoVecProject(point.coord, bestCoords);
26176                             if (choice) {
26177                                 loc = projection.invert(choice.target);
26178                                 graph = graph.replace(node.move(geoVecInterp(node.loc, loc, t)));
26179                             }
26180                         }
26181                     }
26182                 }
26183
26184                 return graph;
26185
26186
26187                 function clonePoints(array) {
26188                     return array.map(function(p) {
26189                         return { id: p.id, coord: [p.coord[0], p.coord[1]] };
26190                     });
26191                 }
26192
26193
26194                 function calcMotion(point, i, array) {
26195                     // don't try to move the endpoints of a non-closed way.
26196                     if (!isClosed && (i === 0 || i === array.length - 1)) { return [0, 0]; }
26197                     // don't try to move a node that appears more than once (self intersection)
26198                     if (nodeCount[array[i].id] > 1) { return [0, 0]; }
26199
26200                     var a = array[(i - 1 + array.length) % array.length].coord;
26201                     var origin = point.coord;
26202                     var b = array[(i + 1) % array.length].coord;
26203                     var p = geoVecSubtract(a, origin);
26204                     var q = geoVecSubtract(b, origin);
26205
26206                     var scale = 2 * Math.min(geoVecLength(p), geoVecLength(q));
26207                     p = geoVecNormalize(p);
26208                     q = geoVecNormalize(q);
26209
26210                     var dotp = (p[0] * q[0] + p[1] * q[1]);
26211                     var val = Math.abs(dotp);
26212
26213                     if (val < lowerThreshold) {  // nearly orthogonal
26214                         corner.i = i;
26215                         corner.dotp = val;
26216                         var vec = geoVecNormalize(geoVecAdd(p, q));
26217                         return geoVecScale(vec, 0.1 * dotp * scale);
26218                     }
26219
26220                     return [0, 0];   // do nothing
26221                 }
26222             };
26223
26224
26225             // if we are only orthogonalizing one vertex,
26226             // get that vertex and the previous and next
26227             function nodeSubset(nodes, vertexID, isClosed) {
26228                 var first = isClosed ? 0 : 1;
26229                 var last = isClosed ? nodes.length : nodes.length - 1;
26230
26231                 for (var i = first; i < last; i++) {
26232                     if (nodes[i].id === vertexID) {
26233                         return [
26234                             nodes[(i - 1 + nodes.length) % nodes.length],
26235                             nodes[i],
26236                             nodes[(i + 1) % nodes.length]
26237                         ];
26238                     }
26239                 }
26240
26241                 return [];
26242             }
26243
26244
26245             action.disabled = function(graph) {
26246                 var way = graph.entity(wayID);
26247                 way = way.removeNode('');  // sanity check - remove any consecutive duplicates
26248                 graph = graph.replace(way);
26249
26250                 var isClosed = way.isClosed();
26251                 var nodes = graph.childNodes(way).slice();  // shallow copy
26252                 if (isClosed) { nodes.pop(); }
26253
26254                 var allowStraightAngles = false;
26255                 if (vertexID !== undefined) {
26256                     allowStraightAngles = true;
26257                     nodes = nodeSubset(nodes, vertexID, isClosed);
26258                     if (nodes.length !== 3) { return 'end_vertex'; }
26259                 }
26260
26261                 var coords = nodes.map(function(n) { return projection(n.loc); });
26262                 var score = geoOrthoCanOrthogonalize(coords, isClosed, epsilon, threshold, allowStraightAngles);
26263
26264                 if (score === null) {
26265                     return 'not_squarish';
26266                 } else if (score === 0) {
26267                     return 'square_enough';
26268                 } else {
26269                     return false;
26270                 }
26271             };
26272
26273
26274             action.transitionable = true;
26275
26276             return action;
26277         }
26278
26279         // `actionRestrictTurn` creates a turn restriction relation.
26280         //
26281         // `turn` must be an `osmTurn` object
26282         // see osm/intersection.js, pathToTurn()
26283         //
26284         // This specifies a restriction of type `restriction` when traveling from
26285         // `turn.from.way` toward `turn.to.way` via `turn.via.node` OR `turn.via.ways`.
26286         // (The action does not check that these entities form a valid intersection.)
26287         //
26288         // From, to, and via ways should be split before calling this action.
26289         // (old versions of the code would split the ways here, but we no longer do it)
26290         //
26291         // For testing convenience, accepts a restrictionID to assign to the new
26292         // relation. Normally, this will be undefined and the relation will
26293         // automatically be assigned a new ID.
26294         //
26295         function actionRestrictTurn(turn, restrictionType, restrictionID) {
26296
26297             return function(graph) {
26298                 var fromWay = graph.entity(turn.from.way);
26299                 var toWay = graph.entity(turn.to.way);
26300                 var viaNode = turn.via.node && graph.entity(turn.via.node);
26301                 var viaWays = turn.via.ways && turn.via.ways.map(function(id) { return graph.entity(id); });
26302                 var members = [];
26303
26304                 members.push({ id: fromWay.id, type: 'way',  role: 'from' });
26305
26306                 if (viaNode) {
26307                     members.push({ id: viaNode.id,  type: 'node', role: 'via' });
26308                 } else if (viaWays) {
26309                     viaWays.forEach(function(viaWay) {
26310                         members.push({ id: viaWay.id,  type: 'way', role: 'via' });
26311                     });
26312                 }
26313
26314                 members.push({ id: toWay.id, type: 'way',  role: 'to' });
26315
26316                 return graph.replace(osmRelation({
26317                     id: restrictionID,
26318                     tags: {
26319                         type: 'restriction',
26320                         restriction: restrictionType
26321                     },
26322                     members: members
26323                 }));
26324             };
26325         }
26326
26327         function actionRevert(id) {
26328             var action = function(graph) {
26329                 var entity = graph.hasEntity(id),
26330                     base = graph.base().entities[id];
26331
26332                 if (entity && !base) {    // entity will be removed..
26333                     if (entity.type === 'node') {
26334                         graph.parentWays(entity)
26335                             .forEach(function(parent) {
26336                                 parent = parent.removeNode(id);
26337                                 graph = graph.replace(parent);
26338
26339                                 if (parent.isDegenerate()) {
26340                                     graph = actionDeleteWay(parent.id)(graph);
26341                                 }
26342                             });
26343                     }
26344
26345                     graph.parentRelations(entity)
26346                         .forEach(function(parent) {
26347                             parent = parent.removeMembersWithID(id);
26348                             graph = graph.replace(parent);
26349
26350                             if (parent.isDegenerate()) {
26351                                 graph = actionDeleteRelation(parent.id)(graph);
26352                             }
26353                         });
26354                 }
26355
26356                 return graph.revert(id);
26357             };
26358
26359             return action;
26360         }
26361
26362         function actionRotate(rotateIds, pivot, angle, projection) {
26363
26364             var action = function(graph) {
26365                 return graph.update(function(graph) {
26366                     utilGetAllNodes(rotateIds, graph).forEach(function(node) {
26367                         var point = geoRotate([projection(node.loc)], angle, pivot)[0];
26368                         graph = graph.replace(node.move(projection.invert(point)));
26369                     });
26370                 });
26371             };
26372
26373             return action;
26374         }
26375
26376         /* Align nodes along their common axis */
26377         function actionStraightenNodes(nodeIDs, projection) {
26378
26379             function positionAlongWay(a, o, b) {
26380                 return geoVecDot(a, b, o) / geoVecDot(b, b, o);
26381             }
26382
26383             // returns the endpoints of the long axis of symmetry of the `points` bounding rect 
26384             function getEndpoints(points) {
26385                 var ssr = geoGetSmallestSurroundingRectangle(points);
26386
26387                 // Choose line pq = axis of symmetry.
26388                 // The shape's surrounding rectangle has 2 axes of symmetry.
26389                 // Snap points to the long axis
26390                 var p1 = [(ssr.poly[0][0] + ssr.poly[1][0]) / 2, (ssr.poly[0][1] + ssr.poly[1][1]) / 2 ];
26391                 var q1 = [(ssr.poly[2][0] + ssr.poly[3][0]) / 2, (ssr.poly[2][1] + ssr.poly[3][1]) / 2 ];
26392                 var p2 = [(ssr.poly[3][0] + ssr.poly[4][0]) / 2, (ssr.poly[3][1] + ssr.poly[4][1]) / 2 ];
26393                 var q2 = [(ssr.poly[1][0] + ssr.poly[2][0]) / 2, (ssr.poly[1][1] + ssr.poly[2][1]) / 2 ];
26394
26395                 var isLong = (geoVecLength(p1, q1) > geoVecLength(p2, q2));
26396                 if (isLong) {
26397                     return [p1, q1];
26398                 }
26399                 return [p2, q2];
26400             }
26401
26402
26403             var action = function(graph, t) {
26404                 if (t === null || !isFinite(t)) { t = 1; }
26405                 t = Math.min(Math.max(+t, 0), 1);
26406
26407                 var nodes = nodeIDs.map(function(id) { return graph.entity(id); });
26408                 var points = nodes.map(function(n) { return projection(n.loc); });
26409                 var endpoints = getEndpoints(points);
26410                 var startPoint = endpoints[0];
26411                 var endPoint = endpoints[1];
26412
26413                 // Move points onto the line connecting the endpoints
26414                 for (var i = 0; i < points.length; i++) {
26415                     var node = nodes[i];
26416                     var point = points[i];
26417                     var u = positionAlongWay(point, startPoint, endPoint);
26418                     var point2 = geoVecInterp(startPoint, endPoint, u);
26419                     var loc2 = projection.invert(point2);
26420                     graph = graph.replace(node.move(geoVecInterp(node.loc, loc2, t)));
26421                 }
26422
26423                 return graph;
26424             };
26425
26426
26427             action.disabled = function(graph) {
26428
26429                 var nodes = nodeIDs.map(function(id) { return graph.entity(id); });
26430                 var points = nodes.map(function(n) { return projection(n.loc); });
26431                 var endpoints = getEndpoints(points);
26432                 var startPoint = endpoints[0];
26433                 var endPoint = endpoints[1];
26434
26435                 var maxDistance = 0;
26436
26437                 for (var i = 0; i < points.length; i++) {
26438                     var point = points[i];
26439                     var u = positionAlongWay(point, startPoint, endPoint);
26440                     var p = geoVecInterp(startPoint, endPoint, u);
26441                     var dist = geoVecLength(p, point);
26442
26443                     if (!isNaN(dist) && dist > maxDistance) {
26444                         maxDistance = dist;
26445                     }
26446                 }
26447
26448                 if (maxDistance < 0.0001) {
26449                     return 'straight_enough';
26450                 }
26451             };
26452
26453
26454             action.transitionable = true;
26455
26456
26457             return action;
26458         }
26459
26460         /*
26461          * Based on https://github.com/openstreetmap/potlatch2/net/systemeD/potlatch2/tools/Straighten.as
26462          */
26463         function actionStraightenWay(selectedIDs, projection) {
26464
26465             function positionAlongWay(a, o, b) {
26466                 return geoVecDot(a, b, o) / geoVecDot(b, b, o);
26467             }
26468
26469             // Return all selected ways as a continuous, ordered array of nodes
26470             function allNodes(graph) {
26471                 var nodes = [];
26472                 var startNodes = [];
26473                 var endNodes = [];
26474                 var remainingWays = [];
26475                 var selectedWays = selectedIDs.filter(function(w) {
26476                     return graph.entity(w).type === 'way';
26477                 });
26478                 var selectedNodes = selectedIDs.filter(function(n) {
26479                     return graph.entity(n).type === 'node';
26480                 });
26481
26482                 for (var i = 0; i < selectedWays.length; i++) {
26483                     var way = graph.entity(selectedWays[i]);
26484                     nodes = way.nodes.slice(0);
26485                     remainingWays.push(nodes);
26486                     startNodes.push(nodes[0]);
26487                     endNodes.push(nodes[nodes.length-1]);
26488                 }
26489
26490                 // Remove duplicate end/startNodes (duplicate nodes cannot be at the line end,
26491                 //   and need to be removed so currNode difference calculation below works)
26492                 // i.e. ["n-1", "n-1", "n-2"] => ["n-2"]
26493                 startNodes = startNodes.filter(function(n) {
26494                     return startNodes.indexOf(n) === startNodes.lastIndexOf(n);
26495                 });
26496                 endNodes = endNodes.filter(function(n) {
26497                     return endNodes.indexOf(n) === endNodes.lastIndexOf(n);
26498                 });
26499
26500                 // Choose the initial endpoint to start from
26501                 var currNode = utilArrayDifference(startNodes, endNodes)
26502                     .concat(utilArrayDifference(endNodes, startNodes))[0];
26503                 var nextWay = [];
26504                 nodes = [];
26505
26506                 // Create nested function outside of loop to avoid "function in loop" lint error
26507                 var getNextWay = function(currNode, remainingWays) {
26508                     return remainingWays.filter(function(way) {
26509                         return way[0] === currNode || way[way.length-1] === currNode;
26510                     })[0];
26511                 };
26512
26513                 // Add nodes to end of nodes array, until all ways are added
26514                 while (remainingWays.length) {
26515                     nextWay = getNextWay(currNode, remainingWays);
26516                     remainingWays = utilArrayDifference(remainingWays, [nextWay]);
26517
26518                     if (nextWay[0] !== currNode) {
26519                         nextWay.reverse();
26520                     }
26521                     nodes = nodes.concat(nextWay);
26522                     currNode = nodes[nodes.length-1];
26523                 }
26524
26525                 // If user selected 2 nodes to straighten between, then slice nodes array to those nodes
26526                 if (selectedNodes.length === 2) {
26527                     var startNodeIdx = nodes.indexOf(selectedNodes[0]);
26528                     var endNodeIdx = nodes.indexOf(selectedNodes[1]);
26529                     var sortedStartEnd = [startNodeIdx, endNodeIdx];
26530
26531                     sortedStartEnd.sort(function(a, b) { return a - b; });
26532                     nodes = nodes.slice(sortedStartEnd[0], sortedStartEnd[1]+1);
26533                 }
26534
26535                 return nodes.map(function(n) { return graph.entity(n); });
26536             }
26537
26538             function shouldKeepNode(node, graph) {
26539                 return graph.parentWays(node).length > 1 ||
26540                     graph.parentRelations(node).length ||
26541                     node.hasInterestingTags();
26542             }
26543
26544
26545             var action = function(graph, t) {
26546                 if (t === null || !isFinite(t)) { t = 1; }
26547                 t = Math.min(Math.max(+t, 0), 1);
26548
26549                 var nodes = allNodes(graph);
26550                 var points = nodes.map(function(n) { return projection(n.loc); });
26551                 var startPoint = points[0];
26552                 var endPoint = points[points.length-1];
26553                 var toDelete = [];
26554                 var i;
26555
26556                 for (i = 1; i < points.length-1; i++) {
26557                     var node = nodes[i];
26558                     var point = points[i];
26559
26560                     if (t < 1 || shouldKeepNode(node, graph)) {
26561                         var u = positionAlongWay(point, startPoint, endPoint);
26562                         var p = geoVecInterp(startPoint, endPoint, u);
26563                         var loc2 = projection.invert(p);
26564                         graph = graph.replace(node.move(geoVecInterp(node.loc, loc2, t)));
26565
26566                     } else {
26567                         // safe to delete
26568                         if (toDelete.indexOf(node) === -1) {
26569                             toDelete.push(node);
26570                         }
26571                     }
26572                 }
26573
26574                 for (i = 0; i < toDelete.length; i++) {
26575                     graph = actionDeleteNode(toDelete[i].id)(graph);
26576                 }
26577
26578                 return graph;
26579             };
26580
26581
26582             action.disabled = function(graph) {
26583                 // check way isn't too bendy
26584                 var nodes = allNodes(graph);
26585                 var points = nodes.map(function(n) { return projection(n.loc); });
26586                 var startPoint = points[0];
26587                 var endPoint = points[points.length-1];
26588                 var threshold = 0.2 * geoVecLength(startPoint, endPoint);
26589                 var i;
26590
26591                 if (threshold === 0) {
26592                     return 'too_bendy';
26593                 }
26594
26595                 var maxDistance = 0;
26596
26597                 for (i = 1; i < points.length - 1; i++) {
26598                     var point = points[i];
26599                     var u = positionAlongWay(point, startPoint, endPoint);
26600                     var p = geoVecInterp(startPoint, endPoint, u);
26601                     var dist = geoVecLength(p, point);
26602
26603                     // to bendy if point is off by 20% of total start/end distance in projected space
26604                     if (isNaN(dist) || dist > threshold) {
26605                         return 'too_bendy';
26606                     } else if (dist > maxDistance) {
26607                         maxDistance = dist;
26608                     }
26609                 }
26610
26611                 var keepingAllNodes = nodes.every(function(node, i) {
26612                     return i === 0 || i === nodes.length - 1 || shouldKeepNode(node, graph);
26613                 });
26614
26615                 if (maxDistance < 0.0001 &&
26616                     // Allow straightening even if already straight in order to remove extraneous nodes
26617                     keepingAllNodes) {
26618                     return 'straight_enough';
26619                 }
26620             };
26621
26622             action.transitionable = true;
26623
26624
26625             return action;
26626         }
26627
26628         // `actionUnrestrictTurn` deletes a turn restriction relation.
26629         //
26630         // `turn` must be an `osmTurn` object with a `restrictionID` property.
26631         // see osm/intersection.js, pathToTurn()
26632         //
26633         function actionUnrestrictTurn(turn) {
26634             return function(graph) {
26635                 return actionDeleteRelation(turn.restrictionID)(graph);
26636             };
26637         }
26638
26639         /* Reflect the given area around its axis of symmetry */
26640         function actionReflect(reflectIds, projection) {
26641             var _useLongAxis = true;
26642
26643
26644             var action = function(graph, t) {
26645                 if (t === null || !isFinite(t)) { t = 1; }
26646                 t = Math.min(Math.max(+t, 0), 1);
26647
26648                 var nodes = utilGetAllNodes(reflectIds, graph);
26649                 var points = nodes.map(function(n) { return projection(n.loc); });
26650                 var ssr = geoGetSmallestSurroundingRectangle(points);
26651
26652                 // Choose line pq = axis of symmetry.
26653                 // The shape's surrounding rectangle has 2 axes of symmetry.
26654                 // Reflect across the longer axis by default.
26655                 var p1 = [(ssr.poly[0][0] + ssr.poly[1][0]) / 2, (ssr.poly[0][1] + ssr.poly[1][1]) / 2 ];
26656                 var q1 = [(ssr.poly[2][0] + ssr.poly[3][0]) / 2, (ssr.poly[2][1] + ssr.poly[3][1]) / 2 ];
26657                 var p2 = [(ssr.poly[3][0] + ssr.poly[4][0]) / 2, (ssr.poly[3][1] + ssr.poly[4][1]) / 2 ];
26658                 var q2 = [(ssr.poly[1][0] + ssr.poly[2][0]) / 2, (ssr.poly[1][1] + ssr.poly[2][1]) / 2 ];
26659                 var p, q;
26660
26661                 var isLong = (geoVecLength(p1, q1) > geoVecLength(p2, q2));
26662                 if ((_useLongAxis && isLong) || (!_useLongAxis && !isLong)) {
26663                     p = p1;
26664                     q = q1;
26665                 } else {
26666                     p = p2;
26667                     q = q2;
26668                 }
26669
26670                 // reflect c across pq
26671                 // http://math.stackexchange.com/questions/65503/point-reflection-over-a-line
26672                 var dx = q[0] - p[0];
26673                 var dy = q[1] - p[1];
26674                 var a = (dx * dx - dy * dy) / (dx * dx + dy * dy);
26675                 var b = 2 * dx * dy / (dx * dx + dy * dy);
26676                 for (var i = 0; i < nodes.length; i++) {
26677                     var node = nodes[i];
26678                     var c = projection(node.loc);
26679                     var c2 = [
26680                         a * (c[0] - p[0]) + b * (c[1] - p[1]) + p[0],
26681                         b * (c[0] - p[0]) - a * (c[1] - p[1]) + p[1]
26682                     ];
26683                     var loc2 = projection.invert(c2);
26684                     node = node.move(geoVecInterp(node.loc, loc2, t));
26685                     graph = graph.replace(node);
26686                 }
26687
26688                 return graph;
26689             };
26690
26691
26692             action.useLongAxis = function(val) {
26693                 if (!arguments.length) { return _useLongAxis; }
26694                 _useLongAxis = val;
26695                 return action;
26696             };
26697
26698
26699             action.transitionable = true;
26700
26701
26702             return action;
26703         }
26704
26705         function actionUpgradeTags(entityId, oldTags, replaceTags) {
26706
26707             return function(graph) {
26708                 var entity = graph.entity(entityId);
26709                 var tags = Object.assign({}, entity.tags);  // shallow copy
26710                 var transferValue;
26711                 var semiIndex;
26712
26713                 for (var oldTagKey in oldTags) {
26714                     if (oldTags[oldTagKey] === '*') {
26715                         transferValue = tags[oldTagKey];
26716                         delete tags[oldTagKey];
26717                     } else {
26718                         var vals = tags[oldTagKey].split(';').filter(Boolean);
26719                         var oldIndex = vals.indexOf(oldTags[oldTagKey]);
26720                         if (vals.length === 1 || oldIndex === -1) {
26721                             delete tags[oldTagKey];
26722                         } else {
26723                             if (replaceTags && replaceTags[oldTagKey]) {
26724                                 // replacing a value within a semicolon-delimited value, note the index
26725                                 semiIndex = oldIndex;
26726                             }
26727                             vals.splice(oldIndex, 1);
26728                             tags[oldTagKey] = vals.join(';');
26729                         }
26730                     }
26731                 }
26732
26733                 if (replaceTags) {
26734                     for (var replaceKey in replaceTags) {
26735                         var replaceValue = replaceTags[replaceKey];
26736                         if (replaceValue === '*') {
26737                             if (tags[replaceKey] && tags[replaceKey] !== 'no') {
26738                                 // allow any pre-existing value except `no` (troll tag)
26739                                 continue;
26740                             } else {
26741                                 // otherwise assume `yes` is okay
26742                                 tags[replaceKey] = 'yes';
26743                             }
26744                         } else if (replaceValue === '$1') {
26745                             tags[replaceKey] = transferValue;
26746                         } else {
26747                             if (tags[replaceKey] && oldTags[replaceKey] && semiIndex !== undefined) {
26748                                 // don't override preexisting values
26749                                 var existingVals = tags[replaceKey].split(';').filter(Boolean);
26750                                 if (existingVals.indexOf(replaceValue) === -1) {
26751                                     existingVals.splice(semiIndex, 0, replaceValue);
26752                                     tags[replaceKey] = existingVals.join(';');
26753                                 }
26754                             } else {
26755                                 tags[replaceKey] = replaceValue;
26756                             }
26757                         }
26758                     }
26759                 }
26760
26761                 return graph.replace(entity.update({ tags: tags }));
26762             };
26763         }
26764
26765         function behaviorEdit(context) {
26766
26767             function behavior() {
26768                 context.map()
26769                     .minzoom(context.minEditableZoom());
26770             }
26771
26772
26773             behavior.off = function() {
26774                 context.map()
26775                     .minzoom(0);
26776             };
26777
26778             return behavior;
26779         }
26780
26781         /*
26782            The hover behavior adds the `.hover` class on pointerover to all elements to which
26783            the identical datum is bound, and removes it on pointerout.
26784
26785            The :hover pseudo-class is insufficient for iD's purposes because a datum's visual
26786            representation may consist of several elements scattered throughout the DOM hierarchy.
26787            Only one of these elements can have the :hover pseudo-class, but all of them will
26788            have the .hover class.
26789          */
26790         function behaviorHover(context) {
26791             var dispatch$1 = dispatch('hover');
26792             var _selection = select(null);
26793             var _newNodeId = null;
26794             var _initialNodeID = null;
26795             var _altDisables;
26796             var _ignoreVertex;
26797             var _targets = [];
26798
26799             // use pointer events on supported platforms; fallback to mouse events
26800             var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
26801
26802
26803             function keydown() {
26804                 if (_altDisables && event.keyCode === utilKeybinding.modifierCodes.alt) {
26805                     _selection.selectAll('.hover')
26806                         .classed('hover-suppressed', true)
26807                         .classed('hover', false);
26808
26809                     _selection
26810                         .classed('hover-disabled', true);
26811
26812                     dispatch$1.call('hover', this, null);
26813                 }
26814             }
26815
26816
26817             function keyup() {
26818                 if (_altDisables && event.keyCode === utilKeybinding.modifierCodes.alt) {
26819                     _selection.selectAll('.hover-suppressed')
26820                         .classed('hover-suppressed', false)
26821                         .classed('hover', true);
26822
26823                     _selection
26824                         .classed('hover-disabled', false);
26825
26826                     dispatch$1.call('hover', this, _targets);
26827                 }
26828             }
26829
26830
26831             function behavior(selection) {
26832                 _selection = selection;
26833
26834                 _targets = [];
26835
26836                 if (_initialNodeID) {
26837                     _newNodeId = _initialNodeID;
26838                     _initialNodeID = null;
26839                 } else {
26840                     _newNodeId = null;
26841                 }
26842
26843                 _selection
26844                     .on(_pointerPrefix + 'over.hover', pointerover)
26845                     .on(_pointerPrefix + 'out.hover', pointerout)
26846                     // treat pointerdown as pointerover for touch devices
26847                     .on(_pointerPrefix + 'down.hover', pointerover);
26848
26849                 select(window)
26850                     .on(_pointerPrefix + 'up.hover pointercancel.hover', pointerout, true)
26851                     .on('keydown.hover', keydown)
26852                     .on('keyup.hover', keyup);
26853
26854
26855                 function eventTarget() {
26856                     var datum = event.target && event.target.__data__;
26857                     if (typeof datum !== 'object') { return null; }
26858                     if (!(datum instanceof osmEntity) && datum.properties && (datum.properties.entity instanceof osmEntity)) {
26859                         return datum.properties.entity;
26860                     }
26861                     return datum;
26862                 }
26863
26864                 function pointerover() {
26865                     // ignore mouse hovers with buttons pressed unless dragging
26866                     if (context.mode().id.indexOf('drag') === -1 &&
26867                         (!event.pointerType || event.pointerType === 'mouse') &&
26868                         event.buttons) { return; }
26869
26870                     var target = eventTarget();
26871                     if (target && _targets.indexOf(target) === -1) {
26872                         _targets.push(target);
26873                         updateHover(_targets);
26874                     }
26875                 }
26876
26877                 function pointerout() {
26878
26879                     var target = eventTarget();
26880                     var index = _targets.indexOf(target);
26881                     if (index !== -1) {
26882                         _targets.splice(index);
26883                         updateHover(_targets);
26884                     }
26885                 }
26886
26887                 function allowsVertex(d) {
26888                     return d.geometry(context.graph()) === 'vertex' || _mainPresetIndex.allowsVertex(d, context.graph());
26889                 }
26890
26891                 function modeAllowsHover(target) {
26892                     var mode = context.mode();
26893                     if (mode.id === 'add-point') {
26894                         return mode.preset.matchGeometry('vertex') ||
26895                             (target.type !== 'way' && target.geometry(context.graph()) !== 'vertex');
26896                     }
26897                     return true;
26898                 }
26899
26900                 function updateHover(targets) {
26901
26902                     _selection.selectAll('.hover')
26903                         .classed('hover', false);
26904                     _selection.selectAll('.hover-suppressed')
26905                         .classed('hover-suppressed', false);
26906
26907                     var mode = context.mode();
26908
26909                     if (!_newNodeId && (mode.id === 'draw-line' || mode.id === 'draw-area')) {
26910                         var node = targets.find(function(target) {
26911                             return target instanceof osmEntity && target.type === 'node';
26912                         });
26913                         _newNodeId = node && node.id;
26914                     }
26915
26916                     targets = targets.filter(function(datum) {
26917                         if (datum instanceof osmEntity) {
26918                             // If drawing a way, don't hover on a node that was just placed. #3974
26919                             return datum.id !== _newNodeId &&
26920                                 (datum.type !== 'node' || !_ignoreVertex || allowsVertex(datum)) &&
26921                                 modeAllowsHover(datum);
26922                         }
26923                         return true;
26924                     });
26925
26926                     var selector = '';
26927
26928                     for (var i in targets) {
26929                         var datum = targets[i];
26930
26931                         // What are we hovering over?
26932                         if (datum.__featurehash__) {
26933                             // hovering custom data
26934                             selector += ', .data' + datum.__featurehash__;
26935
26936                         } else if (datum instanceof QAItem) {
26937                             selector += ', .' + datum.service + '.itemId-' + datum.id;
26938
26939                         } else if (datum instanceof osmNote) {
26940                             selector += ', .note-' + datum.id;
26941
26942                         } else if (datum instanceof osmEntity) {
26943                             selector += ', .' + datum.id;
26944                             if (datum.type === 'relation') {
26945                                 for (var j in datum.members) {
26946                                     selector += ', .' + datum.members[j].id;
26947                                 }
26948                             }
26949                         }
26950                     }
26951
26952                     var suppressed = _altDisables && event && event.altKey;
26953
26954                     if (selector.trim().length) {
26955                         // remove the first comma
26956                         selector = selector.slice(1);
26957                         _selection.selectAll(selector)
26958                             .classed(suppressed ? 'hover-suppressed' : 'hover', true);
26959                     }
26960
26961                     dispatch$1.call('hover', this, !suppressed && targets);
26962                 }
26963             }
26964
26965
26966             behavior.off = function(selection) {
26967                 selection.selectAll('.hover')
26968                     .classed('hover', false);
26969                 selection.selectAll('.hover-suppressed')
26970                     .classed('hover-suppressed', false);
26971                 selection
26972                     .classed('hover-disabled', false);
26973
26974                 selection
26975                     .on(_pointerPrefix + 'over.hover', null)
26976                     .on(_pointerPrefix + 'out.hover', null)
26977                     .on(_pointerPrefix + 'down.hover', null);
26978
26979                 select(window)
26980                     .on(_pointerPrefix + 'up.hover pointercancel.hover', null, true)
26981                     .on('keydown.hover', null)
26982                     .on('keyup.hover', null);
26983             };
26984
26985
26986             behavior.altDisables = function(val) {
26987                 if (!arguments.length) { return _altDisables; }
26988                 _altDisables = val;
26989                 return behavior;
26990             };
26991
26992             behavior.ignoreVertex = function(val) {
26993                 if (!arguments.length) { return _ignoreVertex; }
26994                 _ignoreVertex = val;
26995                 return behavior;
26996             };
26997
26998             behavior.initialNodeID = function(nodeId) {
26999                 _initialNodeID = nodeId;
27000                 return behavior;
27001             };
27002
27003             return utilRebind(behavior, dispatch$1, 'on');
27004         }
27005
27006         var _disableSpace = false;
27007         var _lastSpace = null;
27008
27009
27010         function behaviorDraw(context) {
27011             var dispatch$1 = dispatch(
27012                 'move', 'down', 'downcancel', 'click', 'clickWay', 'clickNode', 'undo', 'cancel', 'finish'
27013             );
27014
27015             var keybinding = utilKeybinding('draw');
27016
27017             var _hover = behaviorHover(context)
27018                 .altDisables(true)
27019                 .ignoreVertex(true)
27020                 .on('hover', context.ui().sidebar.hover);
27021             var _edit = behaviorEdit(context);
27022
27023             var _closeTolerance = 4;
27024             var _tolerance = 12;
27025             var _mouseLeave = false;
27026             var _lastMouse = null;
27027             var _lastPointerUpEvent;
27028
27029             var _downPointer;
27030
27031             // use pointer events on supported platforms; fallback to mouse events
27032             var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
27033
27034
27035             // related code
27036             // - `mode/drag_node.js` `datum()`
27037             function datum() {
27038                 var mode = context.mode();
27039                 var isNote = mode && (mode.id.indexOf('note') !== -1);
27040                 if (event.altKey || isNote) { return {}; }
27041
27042                 var element;
27043                 if (event.type === 'keydown') {
27044                     element = _lastMouse && _lastMouse.target;
27045                 } else {
27046                     element = event.target;
27047                 }
27048
27049                 // When drawing, snap only to touch targets..
27050                 // (this excludes area fills and active drawing elements)
27051                 var d = element.__data__;
27052                 return (d && d.properties && d.properties.target) ? d : {};
27053             }
27054
27055             function pointerdown() {
27056
27057                 if (_downPointer) { return; }
27058
27059                 var pointerLocGetter = utilFastMouse(this);
27060                 _downPointer = {
27061                     id: event.pointerId || 'mouse',
27062                     pointerLocGetter: pointerLocGetter,
27063                     downTime: +new Date(),
27064                     downLoc: pointerLocGetter(event)
27065                 };
27066
27067                 dispatch$1.call('down', this, datum());
27068             }
27069
27070             function pointerup() {
27071
27072                 if (!_downPointer || _downPointer.id !== (event.pointerId || 'mouse')) { return; }
27073
27074                 var downPointer = _downPointer;
27075                 _downPointer = null;
27076
27077                 _lastPointerUpEvent = event;
27078
27079                 if (downPointer.isCancelled) { return; }
27080
27081                 var t2 = +new Date();
27082                 var p2 = downPointer.pointerLocGetter(event);
27083                 var dist = geoVecLength(downPointer.downLoc, p2);
27084
27085                 if (dist < _closeTolerance || (dist < _tolerance && (t2 - downPointer.downTime) < 500)) {
27086                     // Prevent a quick second click
27087                     select(window).on('click.draw-block', function() {
27088                         event.stopPropagation();
27089                     }, true);
27090
27091                     context.map().dblclickZoomEnable(false);
27092
27093                     window.setTimeout(function() {
27094                         context.map().dblclickZoomEnable(true);
27095                         select(window).on('click.draw-block', null);
27096                     }, 500);
27097
27098                     click(p2);
27099                 }
27100             }
27101
27102             function pointermove() {
27103                 if (_downPointer &&
27104                     _downPointer.id === (event.pointerId || 'mouse') &&
27105                     !_downPointer.isCancelled) {
27106                     var p2 = _downPointer.pointerLocGetter(event);
27107                     var dist = geoVecLength(_downPointer.downLoc, p2);
27108                     if (dist >= _closeTolerance) {
27109                         _downPointer.isCancelled = true;
27110                         dispatch$1.call('downcancel', this);
27111                     }
27112                 }
27113
27114                 if ((event.pointerType && event.pointerType !== 'mouse') ||
27115                     event.buttons ||
27116                     _downPointer) { return; }
27117
27118                 // HACK: Mobile Safari likes to send one or more `mouse` type pointermove
27119                 // events immediately after non-mouse pointerup events; detect and ignore them.
27120                 if (_lastPointerUpEvent &&
27121                     _lastPointerUpEvent.pointerType !== 'mouse' &&
27122                     event.timeStamp - _lastPointerUpEvent.timeStamp < 100) { return; }
27123
27124                 _lastMouse = event;
27125                 dispatch$1.call('move', this, datum());
27126             }
27127
27128             function pointercancel() {
27129                 if (_downPointer &&
27130                     _downPointer.id === (event.pointerId || 'mouse')) {
27131
27132                     if (!_downPointer.isCancelled) {
27133                         dispatch$1.call('downcancel', this);
27134                     }
27135                     _downPointer = null;
27136                 }
27137             }
27138
27139             function mouseenter() {
27140                 _mouseLeave = false;
27141             }
27142
27143             function mouseleave() {
27144                 _mouseLeave = true;
27145             }
27146
27147             function allowsVertex(d) {
27148                 return d.geometry(context.graph()) === 'vertex' || _mainPresetIndex.allowsVertex(d, context.graph());
27149             }
27150
27151             // related code
27152             // - `mode/drag_node.js`     `doMove()`
27153             // - `behavior/draw.js`      `click()`
27154             // - `behavior/draw_way.js`  `move()`
27155             function click(loc) {
27156                 var d = datum();
27157                 var target = d && d.properties && d.properties.entity;
27158
27159                 var mode = context.mode();
27160
27161                 if (target && target.type === 'node' && allowsVertex(target)) {   // Snap to a node
27162                     dispatch$1.call('clickNode', this, target, d);
27163                     return;
27164
27165                 } else if (target && target.type === 'way' && (mode.id !== 'add-point' || mode.preset.matchGeometry('vertex'))) {   // Snap to a way
27166                     var choice = geoChooseEdge(
27167                         context.graph().childNodes(target), loc, context.projection, context.activeID()
27168                     );
27169                     if (choice) {
27170                         var edge = [target.nodes[choice.index - 1], target.nodes[choice.index]];
27171                         dispatch$1.call('clickWay', this, choice.loc, edge, d);
27172                         return;
27173                     }
27174                 } else if (mode.id !== 'add-point' || mode.preset.matchGeometry('point')) {
27175                     var locLatLng = context.projection.invert(loc);
27176                     dispatch$1.call('click', this, locLatLng, d);
27177                 }
27178
27179             }
27180
27181             // treat a spacebar press like a click
27182             function space() {
27183                 event.preventDefault();
27184                 event.stopPropagation();
27185
27186                 var currSpace = context.map().mouse();
27187                 if (_disableSpace && _lastSpace) {
27188                     var dist = geoVecLength(_lastSpace, currSpace);
27189                     if (dist > _tolerance) {
27190                         _disableSpace = false;
27191                     }
27192                 }
27193
27194                 if (_disableSpace || _mouseLeave || !_lastMouse) { return; }
27195
27196                 // user must move mouse or release space bar to allow another click
27197                 _lastSpace = currSpace;
27198                 _disableSpace = true;
27199
27200                 select(window).on('keyup.space-block', function() {
27201                     event.preventDefault();
27202                     event.stopPropagation();
27203                     _disableSpace = false;
27204                     select(window).on('keyup.space-block', null);
27205                 });
27206
27207                 // get the current mouse position
27208                 var loc = context.map().mouse() ||
27209                     // or the map center if the mouse has never entered the map
27210                     context.projection(context.map().center());
27211                 click(loc);
27212             }
27213
27214
27215             function backspace() {
27216                 event.preventDefault();
27217                 dispatch$1.call('undo');
27218             }
27219
27220
27221             function del() {
27222                 event.preventDefault();
27223                 dispatch$1.call('cancel');
27224             }
27225
27226
27227             function ret() {
27228                 event.preventDefault();
27229                 dispatch$1.call('finish');
27230             }
27231
27232
27233             function behavior(selection) {
27234                 context.install(_hover);
27235                 context.install(_edit);
27236
27237                 _downPointer = null;
27238
27239                 keybinding
27240                     .on('⌫', backspace)
27241                     .on('⌦', del)
27242                     .on('⎋', ret)
27243                     .on('↩', ret)
27244                     .on('space', space)
27245                     .on('⌥space', space);
27246
27247                 selection
27248                     .on('mouseenter.draw', mouseenter)
27249                     .on('mouseleave.draw', mouseleave)
27250                     .on(_pointerPrefix + 'down.draw', pointerdown)
27251                     .on(_pointerPrefix + 'move.draw', pointermove);
27252
27253                 select(window)
27254                     .on(_pointerPrefix + 'up.draw', pointerup, true)
27255                     .on('pointercancel.draw', pointercancel, true);
27256
27257                 select(document)
27258                     .call(keybinding);
27259
27260                 return behavior;
27261             }
27262
27263
27264             behavior.off = function(selection) {
27265                 context.ui().sidebar.hover.cancel();
27266                 context.uninstall(_hover);
27267                 context.uninstall(_edit);
27268
27269                 selection
27270                     .on('mouseenter.draw', null)
27271                     .on('mouseleave.draw', null)
27272                     .on(_pointerPrefix + 'down.draw', null)
27273                     .on(_pointerPrefix + 'move.draw', null);
27274
27275                 select(window)
27276                     .on(_pointerPrefix + 'up.draw', null)
27277                     .on('pointercancel.draw', null);
27278                     // note: keyup.space-block, click.draw-block should remain
27279
27280                 select(document)
27281                     .call(keybinding.unbind);
27282             };
27283
27284
27285             behavior.hover = function() {
27286                 return _hover;
27287             };
27288
27289
27290             return utilRebind(behavior, dispatch$1, 'on');
27291         }
27292
27293         function initRange(domain, range) {
27294           switch (arguments.length) {
27295             case 0: break;
27296             case 1: this.range(domain); break;
27297             default: this.range(range).domain(domain); break;
27298           }
27299           return this;
27300         }
27301
27302         var prefix = "$";
27303
27304         function Map$1() {}
27305
27306         Map$1.prototype = map$2.prototype = {
27307           constructor: Map$1,
27308           has: function(key) {
27309             return (prefix + key) in this;
27310           },
27311           get: function(key) {
27312             return this[prefix + key];
27313           },
27314           set: function(key, value) {
27315             this[prefix + key] = value;
27316             return this;
27317           },
27318           remove: function(key) {
27319             var property = prefix + key;
27320             return property in this && delete this[property];
27321           },
27322           clear: function() {
27323             for (var property in this) { if (property[0] === prefix) { delete this[property]; } }
27324           },
27325           keys: function() {
27326             var keys = [];
27327             for (var property in this) { if (property[0] === prefix) { keys.push(property.slice(1)); } }
27328             return keys;
27329           },
27330           values: function() {
27331             var values = [];
27332             for (var property in this) { if (property[0] === prefix) { values.push(this[property]); } }
27333             return values;
27334           },
27335           entries: function() {
27336             var entries = [];
27337             for (var property in this) { if (property[0] === prefix) { entries.push({key: property.slice(1), value: this[property]}); } }
27338             return entries;
27339           },
27340           size: function() {
27341             var size = 0;
27342             for (var property in this) { if (property[0] === prefix) { ++size; } }
27343             return size;
27344           },
27345           empty: function() {
27346             for (var property in this) { if (property[0] === prefix) { return false; } }
27347             return true;
27348           },
27349           each: function(f) {
27350             for (var property in this) { if (property[0] === prefix) { f(this[property], property.slice(1), this); } }
27351           }
27352         };
27353
27354         function map$2(object, f) {
27355           var map = new Map$1;
27356
27357           // Copy constructor.
27358           if (object instanceof Map$1) { object.each(function(value, key) { map.set(key, value); }); }
27359
27360           // Index array by numeric index or specified key function.
27361           else if (Array.isArray(object)) {
27362             var i = -1,
27363                 n = object.length,
27364                 o;
27365
27366             if (f == null) { while (++i < n) { map.set(i, object[i]); } }
27367             else { while (++i < n) { map.set(f(o = object[i], i, object), o); } }
27368           }
27369
27370           // Convert object to map.
27371           else if (object) { for (var key in object) { map.set(key, object[key]); } }
27372
27373           return map;
27374         }
27375
27376         function Set$1() {}
27377
27378         var proto = map$2.prototype;
27379
27380         Set$1.prototype = set$2.prototype = {
27381           constructor: Set$1,
27382           has: proto.has,
27383           add: function(value) {
27384             value += "";
27385             this[prefix + value] = value;
27386             return this;
27387           },
27388           remove: proto.remove,
27389           clear: proto.clear,
27390           values: proto.keys,
27391           size: proto.size,
27392           empty: proto.empty,
27393           each: proto.each
27394         };
27395
27396         function set$2(object, f) {
27397           var set = new Set$1;
27398
27399           // Copy constructor.
27400           if (object instanceof Set$1) { object.each(function(value) { set.add(value); }); }
27401
27402           // Otherwise, assume it’s an array.
27403           else if (object) {
27404             var i = -1, n = object.length;
27405             if (f == null) { while (++i < n) { set.add(object[i]); } }
27406             else { while (++i < n) { set.add(f(object[i], i, object)); } }
27407           }
27408
27409           return set;
27410         }
27411
27412         var array$1 = Array.prototype;
27413
27414         var map$3 = array$1.map;
27415         var slice$4 = array$1.slice;
27416
27417         function constant$4(x) {
27418           return function() {
27419             return x;
27420           };
27421         }
27422
27423         function number$1(x) {
27424           return +x;
27425         }
27426
27427         var unit = [0, 1];
27428
27429         function identity$3(x) {
27430           return x;
27431         }
27432
27433         function normalize(a, b) {
27434           return (b -= (a = +a))
27435               ? function(x) { return (x - a) / b; }
27436               : constant$4(isNaN(b) ? NaN : 0.5);
27437         }
27438
27439         function clamper(domain) {
27440           var a = domain[0], b = domain[domain.length - 1], t;
27441           if (a > b) { t = a, a = b, b = t; }
27442           return function(x) { return Math.max(a, Math.min(b, x)); };
27443         }
27444
27445         // normalize(a, b)(x) takes a domain value x in [a,b] and returns the corresponding parameter t in [0,1].
27446         // interpolate(a, b)(t) takes a parameter t in [0,1] and returns the corresponding range value x in [a,b].
27447         function bimap(domain, range, interpolate) {
27448           var d0 = domain[0], d1 = domain[1], r0 = range[0], r1 = range[1];
27449           if (d1 < d0) { d0 = normalize(d1, d0), r0 = interpolate(r1, r0); }
27450           else { d0 = normalize(d0, d1), r0 = interpolate(r0, r1); }
27451           return function(x) { return r0(d0(x)); };
27452         }
27453
27454         function polymap(domain, range, interpolate) {
27455           var j = Math.min(domain.length, range.length) - 1,
27456               d = new Array(j),
27457               r = new Array(j),
27458               i = -1;
27459
27460           // Reverse descending domains.
27461           if (domain[j] < domain[0]) {
27462             domain = domain.slice().reverse();
27463             range = range.slice().reverse();
27464           }
27465
27466           while (++i < j) {
27467             d[i] = normalize(domain[i], domain[i + 1]);
27468             r[i] = interpolate(range[i], range[i + 1]);
27469           }
27470
27471           return function(x) {
27472             var i = bisectRight(domain, x, 1, j) - 1;
27473             return r[i](d[i](x));
27474           };
27475         }
27476
27477         function copy$1(source, target) {
27478           return target
27479               .domain(source.domain())
27480               .range(source.range())
27481               .interpolate(source.interpolate())
27482               .clamp(source.clamp())
27483               .unknown(source.unknown());
27484         }
27485
27486         function transformer$1() {
27487           var domain = unit,
27488               range = unit,
27489               interpolate$1 = interpolate,
27490               transform,
27491               untransform,
27492               unknown,
27493               clamp = identity$3,
27494               piecewise,
27495               output,
27496               input;
27497
27498           function rescale() {
27499             piecewise = Math.min(domain.length, range.length) > 2 ? polymap : bimap;
27500             output = input = null;
27501             return scale;
27502           }
27503
27504           function scale(x) {
27505             return isNaN(x = +x) ? unknown : (output || (output = piecewise(domain.map(transform), range, interpolate$1)))(transform(clamp(x)));
27506           }
27507
27508           scale.invert = function(y) {
27509             return clamp(untransform((input || (input = piecewise(range, domain.map(transform), d3_interpolateNumber)))(y)));
27510           };
27511
27512           scale.domain = function(_) {
27513             return arguments.length ? (domain = map$3.call(_, number$1), clamp === identity$3 || (clamp = clamper(domain)), rescale()) : domain.slice();
27514           };
27515
27516           scale.range = function(_) {
27517             return arguments.length ? (range = slice$4.call(_), rescale()) : range.slice();
27518           };
27519
27520           scale.rangeRound = function(_) {
27521             return range = slice$4.call(_), interpolate$1 = interpolateRound, rescale();
27522           };
27523
27524           scale.clamp = function(_) {
27525             return arguments.length ? (clamp = _ ? clamper(domain) : identity$3, scale) : clamp !== identity$3;
27526           };
27527
27528           scale.interpolate = function(_) {
27529             return arguments.length ? (interpolate$1 = _, rescale()) : interpolate$1;
27530           };
27531
27532           scale.unknown = function(_) {
27533             return arguments.length ? (unknown = _, scale) : unknown;
27534           };
27535
27536           return function(t, u) {
27537             transform = t, untransform = u;
27538             return rescale();
27539           };
27540         }
27541
27542         function continuous(transform, untransform) {
27543           return transformer$1()(transform, untransform);
27544         }
27545
27546         // Computes the decimal coefficient and exponent of the specified number x with
27547         // significant digits p, where x is positive and p is in [1, 21] or undefined.
27548         // For example, formatDecimal(1.23) returns ["123", 0].
27549         function formatDecimal(x, p) {
27550           if ((i = (x = p ? x.toExponential(p - 1) : x.toExponential()).indexOf("e")) < 0) { return null; } // NaN, ±Infinity
27551           var i, coefficient = x.slice(0, i);
27552
27553           // The string returned by toExponential either has the form \d\.\d+e[-+]\d+
27554           // (e.g., 1.2e+3) or the form \de[-+]\d+ (e.g., 1e+3).
27555           return [
27556             coefficient.length > 1 ? coefficient[0] + coefficient.slice(2) : coefficient,
27557             +x.slice(i + 1)
27558           ];
27559         }
27560
27561         function exponent(x) {
27562           return x = formatDecimal(Math.abs(x)), x ? x[1] : NaN;
27563         }
27564
27565         function formatGroup(grouping, thousands) {
27566           return function(value, width) {
27567             var i = value.length,
27568                 t = [],
27569                 j = 0,
27570                 g = grouping[0],
27571                 length = 0;
27572
27573             while (i > 0 && g > 0) {
27574               if (length + g + 1 > width) { g = Math.max(1, width - length); }
27575               t.push(value.substring(i -= g, i + g));
27576               if ((length += g + 1) > width) { break; }
27577               g = grouping[j = (j + 1) % grouping.length];
27578             }
27579
27580             return t.reverse().join(thousands);
27581           };
27582         }
27583
27584         function formatNumerals(numerals) {
27585           return function(value) {
27586             return value.replace(/[0-9]/g, function(i) {
27587               return numerals[+i];
27588             });
27589           };
27590         }
27591
27592         // [[fill]align][sign][symbol][0][width][,][.precision][~][type]
27593         var re = /^(?:(.)?([<>=^]))?([+\-( ])?([$#])?(0)?(\d+)?(,)?(\.\d+)?(~)?([a-z%])?$/i;
27594
27595         function formatSpecifier(specifier) {
27596           if (!(match = re.exec(specifier))) { throw new Error("invalid format: " + specifier); }
27597           var match;
27598           return new FormatSpecifier({
27599             fill: match[1],
27600             align: match[2],
27601             sign: match[3],
27602             symbol: match[4],
27603             zero: match[5],
27604             width: match[6],
27605             comma: match[7],
27606             precision: match[8] && match[8].slice(1),
27607             trim: match[9],
27608             type: match[10]
27609           });
27610         }
27611
27612         formatSpecifier.prototype = FormatSpecifier.prototype; // instanceof
27613
27614         function FormatSpecifier(specifier) {
27615           this.fill = specifier.fill === undefined ? " " : specifier.fill + "";
27616           this.align = specifier.align === undefined ? ">" : specifier.align + "";
27617           this.sign = specifier.sign === undefined ? "-" : specifier.sign + "";
27618           this.symbol = specifier.symbol === undefined ? "" : specifier.symbol + "";
27619           this.zero = !!specifier.zero;
27620           this.width = specifier.width === undefined ? undefined : +specifier.width;
27621           this.comma = !!specifier.comma;
27622           this.precision = specifier.precision === undefined ? undefined : +specifier.precision;
27623           this.trim = !!specifier.trim;
27624           this.type = specifier.type === undefined ? "" : specifier.type + "";
27625         }
27626
27627         FormatSpecifier.prototype.toString = function() {
27628           return this.fill
27629               + this.align
27630               + this.sign
27631               + this.symbol
27632               + (this.zero ? "0" : "")
27633               + (this.width === undefined ? "" : Math.max(1, this.width | 0))
27634               + (this.comma ? "," : "")
27635               + (this.precision === undefined ? "" : "." + Math.max(0, this.precision | 0))
27636               + (this.trim ? "~" : "")
27637               + this.type;
27638         };
27639
27640         // Trims insignificant zeros, e.g., replaces 1.2000k with 1.2k.
27641         function formatTrim(s) {
27642           out: for (var n = s.length, i = 1, i0 = -1, i1; i < n; ++i) {
27643             switch (s[i]) {
27644               case ".": i0 = i1 = i; break;
27645               case "0": if (i0 === 0) { i0 = i; } i1 = i; break;
27646               default: if (!+s[i]) { break out; } if (i0 > 0) { i0 = 0; } break;
27647             }
27648           }
27649           return i0 > 0 ? s.slice(0, i0) + s.slice(i1 + 1) : s;
27650         }
27651
27652         var prefixExponent;
27653
27654         function formatPrefixAuto(x, p) {
27655           var d = formatDecimal(x, p);
27656           if (!d) { return x + ""; }
27657           var coefficient = d[0],
27658               exponent = d[1],
27659               i = exponent - (prefixExponent = Math.max(-8, Math.min(8, Math.floor(exponent / 3))) * 3) + 1,
27660               n = coefficient.length;
27661           return i === n ? coefficient
27662               : i > n ? coefficient + new Array(i - n + 1).join("0")
27663               : i > 0 ? coefficient.slice(0, i) + "." + coefficient.slice(i)
27664               : "0." + new Array(1 - i).join("0") + formatDecimal(x, Math.max(0, p + i - 1))[0]; // less than 1y!
27665         }
27666
27667         function formatRounded(x, p) {
27668           var d = formatDecimal(x, p);
27669           if (!d) { return x + ""; }
27670           var coefficient = d[0],
27671               exponent = d[1];
27672           return exponent < 0 ? "0." + new Array(-exponent).join("0") + coefficient
27673               : coefficient.length > exponent + 1 ? coefficient.slice(0, exponent + 1) + "." + coefficient.slice(exponent + 1)
27674               : coefficient + new Array(exponent - coefficient.length + 2).join("0");
27675         }
27676
27677         var formatTypes = {
27678           "%": function(x, p) { return (x * 100).toFixed(p); },
27679           "b": function(x) { return Math.round(x).toString(2); },
27680           "c": function(x) { return x + ""; },
27681           "d": function(x) { return Math.round(x).toString(10); },
27682           "e": function(x, p) { return x.toExponential(p); },
27683           "f": function(x, p) { return x.toFixed(p); },
27684           "g": function(x, p) { return x.toPrecision(p); },
27685           "o": function(x) { return Math.round(x).toString(8); },
27686           "p": function(x, p) { return formatRounded(x * 100, p); },
27687           "r": formatRounded,
27688           "s": formatPrefixAuto,
27689           "X": function(x) { return Math.round(x).toString(16).toUpperCase(); },
27690           "x": function(x) { return Math.round(x).toString(16); }
27691         };
27692
27693         function identity$4(x) {
27694           return x;
27695         }
27696
27697         var map$4 = Array.prototype.map,
27698             prefixes = ["y","z","a","f","p","n","µ","m","","k","M","G","T","P","E","Z","Y"];
27699
27700         function formatLocale(locale) {
27701           var group = locale.grouping === undefined || locale.thousands === undefined ? identity$4 : formatGroup(map$4.call(locale.grouping, Number), locale.thousands + ""),
27702               currencyPrefix = locale.currency === undefined ? "" : locale.currency[0] + "",
27703               currencySuffix = locale.currency === undefined ? "" : locale.currency[1] + "",
27704               decimal = locale.decimal === undefined ? "." : locale.decimal + "",
27705               numerals = locale.numerals === undefined ? identity$4 : formatNumerals(map$4.call(locale.numerals, String)),
27706               percent = locale.percent === undefined ? "%" : locale.percent + "",
27707               minus = locale.minus === undefined ? "-" : locale.minus + "",
27708               nan = locale.nan === undefined ? "NaN" : locale.nan + "";
27709
27710           function newFormat(specifier) {
27711             specifier = formatSpecifier(specifier);
27712
27713             var fill = specifier.fill,
27714                 align = specifier.align,
27715                 sign = specifier.sign,
27716                 symbol = specifier.symbol,
27717                 zero = specifier.zero,
27718                 width = specifier.width,
27719                 comma = specifier.comma,
27720                 precision = specifier.precision,
27721                 trim = specifier.trim,
27722                 type = specifier.type;
27723
27724             // The "n" type is an alias for ",g".
27725             if (type === "n") { comma = true, type = "g"; }
27726
27727             // The "" type, and any invalid type, is an alias for ".12~g".
27728             else if (!formatTypes[type]) { precision === undefined && (precision = 12), trim = true, type = "g"; }
27729
27730             // If zero fill is specified, padding goes after sign and before digits.
27731             if (zero || (fill === "0" && align === "=")) { zero = true, fill = "0", align = "="; }
27732
27733             // Compute the prefix and suffix.
27734             // For SI-prefix, the suffix is lazily computed.
27735             var prefix = symbol === "$" ? currencyPrefix : symbol === "#" && /[boxX]/.test(type) ? "0" + type.toLowerCase() : "",
27736                 suffix = symbol === "$" ? currencySuffix : /[%p]/.test(type) ? percent : "";
27737
27738             // What format function should we use?
27739             // Is this an integer type?
27740             // Can this type generate exponential notation?
27741             var formatType = formatTypes[type],
27742                 maybeSuffix = /[defgprs%]/.test(type);
27743
27744             // Set the default precision if not specified,
27745             // or clamp the specified precision to the supported range.
27746             // For significant precision, it must be in [1, 21].
27747             // For fixed precision, it must be in [0, 20].
27748             precision = precision === undefined ? 6
27749                 : /[gprs]/.test(type) ? Math.max(1, Math.min(21, precision))
27750                 : Math.max(0, Math.min(20, precision));
27751
27752             function format(value) {
27753               var valuePrefix = prefix,
27754                   valueSuffix = suffix,
27755                   i, n, c;
27756
27757               if (type === "c") {
27758                 valueSuffix = formatType(value) + valueSuffix;
27759                 value = "";
27760               } else {
27761                 value = +value;
27762
27763                 // Determine the sign. -0 is not less than 0, but 1 / -0 is!
27764                 var valueNegative = value < 0 || 1 / value < 0;
27765
27766                 // Perform the initial formatting.
27767                 value = isNaN(value) ? nan : formatType(Math.abs(value), precision);
27768
27769                 // Trim insignificant zeros.
27770                 if (trim) { value = formatTrim(value); }
27771
27772                 // If a negative value rounds to zero after formatting, and no explicit positive sign is requested, hide the sign.
27773                 if (valueNegative && +value === 0 && sign !== "+") { valueNegative = false; }
27774
27775                 // Compute the prefix and suffix.
27776                 valuePrefix = (valueNegative ? (sign === "(" ? sign : minus) : sign === "-" || sign === "(" ? "" : sign) + valuePrefix;
27777                 valueSuffix = (type === "s" ? prefixes[8 + prefixExponent / 3] : "") + valueSuffix + (valueNegative && sign === "(" ? ")" : "");
27778
27779                 // Break the formatted value into the integer “value” part that can be
27780                 // grouped, and fractional or exponential “suffix” part that is not.
27781                 if (maybeSuffix) {
27782                   i = -1, n = value.length;
27783                   while (++i < n) {
27784                     if (c = value.charCodeAt(i), 48 > c || c > 57) {
27785                       valueSuffix = (c === 46 ? decimal + value.slice(i + 1) : value.slice(i)) + valueSuffix;
27786                       value = value.slice(0, i);
27787                       break;
27788                     }
27789                   }
27790                 }
27791               }
27792
27793               // If the fill character is not "0", grouping is applied before padding.
27794               if (comma && !zero) { value = group(value, Infinity); }
27795
27796               // Compute the padding.
27797               var length = valuePrefix.length + value.length + valueSuffix.length,
27798                   padding = length < width ? new Array(width - length + 1).join(fill) : "";
27799
27800               // If the fill character is "0", grouping is applied after padding.
27801               if (comma && zero) { value = group(padding + value, padding.length ? width - valueSuffix.length : Infinity), padding = ""; }
27802
27803               // Reconstruct the final output based on the desired alignment.
27804               switch (align) {
27805                 case "<": value = valuePrefix + value + valueSuffix + padding; break;
27806                 case "=": value = valuePrefix + padding + value + valueSuffix; break;
27807                 case "^": value = padding.slice(0, length = padding.length >> 1) + valuePrefix + value + valueSuffix + padding.slice(length); break;
27808                 default: value = padding + valuePrefix + value + valueSuffix; break;
27809               }
27810
27811               return numerals(value);
27812             }
27813
27814             format.toString = function() {
27815               return specifier + "";
27816             };
27817
27818             return format;
27819           }
27820
27821           function formatPrefix(specifier, value) {
27822             var f = newFormat((specifier = formatSpecifier(specifier), specifier.type = "f", specifier)),
27823                 e = Math.max(-8, Math.min(8, Math.floor(exponent(value) / 3))) * 3,
27824                 k = Math.pow(10, -e),
27825                 prefix = prefixes[8 + e / 3];
27826             return function(value) {
27827               return f(k * value) + prefix;
27828             };
27829           }
27830
27831           return {
27832             format: newFormat,
27833             formatPrefix: formatPrefix
27834           };
27835         }
27836
27837         var locale;
27838         var format;
27839         var formatPrefix;
27840
27841         defaultLocale({
27842           decimal: ".",
27843           thousands: ",",
27844           grouping: [3],
27845           currency: ["$", ""],
27846           minus: "-"
27847         });
27848
27849         function defaultLocale(definition) {
27850           locale = formatLocale(definition);
27851           format = locale.format;
27852           formatPrefix = locale.formatPrefix;
27853           return locale;
27854         }
27855
27856         function precisionFixed(step) {
27857           return Math.max(0, -exponent(Math.abs(step)));
27858         }
27859
27860         function precisionPrefix(step, value) {
27861           return Math.max(0, Math.max(-8, Math.min(8, Math.floor(exponent(value) / 3))) * 3 - exponent(Math.abs(step)));
27862         }
27863
27864         function precisionRound(step, max) {
27865           step = Math.abs(step), max = Math.abs(max) - step;
27866           return Math.max(0, exponent(max) - exponent(step)) + 1;
27867         }
27868
27869         function tickFormat(start, stop, count, specifier) {
27870           var step = tickStep(start, stop, count),
27871               precision;
27872           specifier = formatSpecifier(specifier == null ? ",f" : specifier);
27873           switch (specifier.type) {
27874             case "s": {
27875               var value = Math.max(Math.abs(start), Math.abs(stop));
27876               if (specifier.precision == null && !isNaN(precision = precisionPrefix(step, value))) { specifier.precision = precision; }
27877               return formatPrefix(specifier, value);
27878             }
27879             case "":
27880             case "e":
27881             case "g":
27882             case "p":
27883             case "r": {
27884               if (specifier.precision == null && !isNaN(precision = precisionRound(step, Math.max(Math.abs(start), Math.abs(stop))))) { specifier.precision = precision - (specifier.type === "e"); }
27885               break;
27886             }
27887             case "f":
27888             case "%": {
27889               if (specifier.precision == null && !isNaN(precision = precisionFixed(step))) { specifier.precision = precision - (specifier.type === "%") * 2; }
27890               break;
27891             }
27892           }
27893           return format(specifier);
27894         }
27895
27896         function linearish(scale) {
27897           var domain = scale.domain;
27898
27899           scale.ticks = function(count) {
27900             var d = domain();
27901             return ticks(d[0], d[d.length - 1], count == null ? 10 : count);
27902           };
27903
27904           scale.tickFormat = function(count, specifier) {
27905             var d = domain();
27906             return tickFormat(d[0], d[d.length - 1], count == null ? 10 : count, specifier);
27907           };
27908
27909           scale.nice = function(count) {
27910             if (count == null) { count = 10; }
27911
27912             var d = domain(),
27913                 i0 = 0,
27914                 i1 = d.length - 1,
27915                 start = d[i0],
27916                 stop = d[i1],
27917                 step;
27918
27919             if (stop < start) {
27920               step = start, start = stop, stop = step;
27921               step = i0, i0 = i1, i1 = step;
27922             }
27923
27924             step = tickIncrement(start, stop, count);
27925
27926             if (step > 0) {
27927               start = Math.floor(start / step) * step;
27928               stop = Math.ceil(stop / step) * step;
27929               step = tickIncrement(start, stop, count);
27930             } else if (step < 0) {
27931               start = Math.ceil(start * step) / step;
27932               stop = Math.floor(stop * step) / step;
27933               step = tickIncrement(start, stop, count);
27934             }
27935
27936             if (step > 0) {
27937               d[i0] = Math.floor(start / step) * step;
27938               d[i1] = Math.ceil(stop / step) * step;
27939               domain(d);
27940             } else if (step < 0) {
27941               d[i0] = Math.ceil(start * step) / step;
27942               d[i1] = Math.floor(stop * step) / step;
27943               domain(d);
27944             }
27945
27946             return scale;
27947           };
27948
27949           return scale;
27950         }
27951
27952         function linear$2() {
27953           var scale = continuous(identity$3, identity$3);
27954
27955           scale.copy = function() {
27956             return copy$1(scale, linear$2());
27957           };
27958
27959           initRange.apply(scale, arguments);
27960
27961           return linearish(scale);
27962         }
27963
27964         function quantize() {
27965           var x0 = 0,
27966               x1 = 1,
27967               n = 1,
27968               domain = [0.5],
27969               range = [0, 1],
27970               unknown;
27971
27972           function scale(x) {
27973             return x <= x ? range[bisectRight(domain, x, 0, n)] : unknown;
27974           }
27975
27976           function rescale() {
27977             var i = -1;
27978             domain = new Array(n);
27979             while (++i < n) { domain[i] = ((i + 1) * x1 - (i - n) * x0) / (n + 1); }
27980             return scale;
27981           }
27982
27983           scale.domain = function(_) {
27984             return arguments.length ? (x0 = +_[0], x1 = +_[1], rescale()) : [x0, x1];
27985           };
27986
27987           scale.range = function(_) {
27988             return arguments.length ? (n = (range = slice$4.call(_)).length - 1, rescale()) : range.slice();
27989           };
27990
27991           scale.invertExtent = function(y) {
27992             var i = range.indexOf(y);
27993             return i < 0 ? [NaN, NaN]
27994                 : i < 1 ? [x0, domain[0]]
27995                 : i >= n ? [domain[n - 1], x1]
27996                 : [domain[i - 1], domain[i]];
27997           };
27998
27999           scale.unknown = function(_) {
28000             return arguments.length ? (unknown = _, scale) : scale;
28001           };
28002
28003           scale.thresholds = function() {
28004             return domain.slice();
28005           };
28006
28007           scale.copy = function() {
28008             return quantize()
28009                 .domain([x0, x1])
28010                 .range(range)
28011                 .unknown(unknown);
28012           };
28013
28014           return initRange.apply(linearish(scale), arguments);
28015         }
28016
28017         function behaviorBreathe() {
28018             var duration = 800;
28019             var steps = 4;
28020             var selector = '.selected.shadow, .selected .shadow';
28021             var _selected = select(null);
28022             var _classed = '';
28023             var _params = {};
28024             var _done = false;
28025             var _timer;
28026
28027
28028             function ratchetyInterpolator(a, b, steps, units) {
28029                 a = parseFloat(a);
28030                 b = parseFloat(b);
28031                 var sample = quantize()
28032                     .domain([0, 1])
28033                     .range(d3_quantize(d3_interpolateNumber(a, b), steps));
28034
28035                 return function(t) {
28036                     return String(sample(t)) + (units || '');
28037                 };
28038             }
28039
28040
28041             function reset(selection) {
28042                 selection
28043                     .style('stroke-opacity', null)
28044                     .style('stroke-width', null)
28045                     .style('fill-opacity', null)
28046                     .style('r', null);
28047             }
28048
28049
28050             function setAnimationParams(transition, fromTo) {
28051                 var toFrom = (fromTo === 'from' ? 'to' : 'from');
28052
28053                 transition
28054                     .styleTween('stroke-opacity', function(d) {
28055                         return ratchetyInterpolator(
28056                             _params[d.id][toFrom].opacity,
28057                             _params[d.id][fromTo].opacity,
28058                             steps
28059                         );
28060                     })
28061                     .styleTween('stroke-width', function(d) {
28062                         return ratchetyInterpolator(
28063                             _params[d.id][toFrom].width,
28064                             _params[d.id][fromTo].width,
28065                             steps,
28066                             'px'
28067                         );
28068                     })
28069                     .styleTween('fill-opacity', function(d) {
28070                         return ratchetyInterpolator(
28071                             _params[d.id][toFrom].opacity,
28072                             _params[d.id][fromTo].opacity,
28073                             steps
28074                         );
28075                     })
28076                     .styleTween('r', function(d) {
28077                         return ratchetyInterpolator(
28078                             _params[d.id][toFrom].width,
28079                             _params[d.id][fromTo].width,
28080                             steps,
28081                             'px'
28082                         );
28083                     });
28084             }
28085
28086
28087             function calcAnimationParams(selection) {
28088                 selection
28089                     .call(reset)
28090                     .each(function(d) {
28091                         var s = select(this);
28092                         var tag = s.node().tagName;
28093                         var p = {'from': {}, 'to': {}};
28094                         var opacity;
28095                         var width;
28096
28097                         // determine base opacity and width
28098                         if (tag === 'circle') {
28099                             opacity = parseFloat(s.style('fill-opacity') || 0.5);
28100                             width = parseFloat(s.style('r') || 15.5);
28101                         } else {
28102                             opacity = parseFloat(s.style('stroke-opacity') || 0.7);
28103                             width = parseFloat(s.style('stroke-width') || 10);
28104                         }
28105
28106                         // calculate from/to interpolation params..
28107                         p.tag = tag;
28108                         p.from.opacity = opacity * 0.6;
28109                         p.to.opacity = opacity * 1.25;
28110                         p.from.width = width * 0.7;
28111                         p.to.width = width * (tag === 'circle' ? 1.5 : 1);
28112                         _params[d.id] = p;
28113                     });
28114             }
28115
28116
28117             function run(surface, fromTo) {
28118                 var toFrom = (fromTo === 'from' ? 'to' : 'from');
28119                 var currSelected = surface.selectAll(selector);
28120                 var currClassed = surface.attr('class');
28121
28122                 if (_done || currSelected.empty()) {
28123                     _selected.call(reset);
28124                     _selected = select(null);
28125                     return;
28126                 }
28127
28128                 if (!fastDeepEqual(currSelected.data(), _selected.data()) || currClassed !== _classed) {
28129                     _selected.call(reset);
28130                     _classed = currClassed;
28131                     _selected = currSelected.call(calcAnimationParams);
28132                 }
28133
28134                 var didCallNextRun = false;
28135
28136                 _selected
28137                     .transition()
28138                     .duration(duration)
28139                     .call(setAnimationParams, fromTo)
28140                     .on('end', function() {
28141                         // `end` event is called for each selected element, but we want
28142                         // it to run only once
28143                         if (!didCallNextRun) {
28144                             surface.call(run, toFrom);
28145                             didCallNextRun = true;
28146                         }
28147
28148                         // if entity was deselected, remove breathe styling
28149                         if (!select(this).classed('selected')) {
28150                             reset(select(this));
28151                         }
28152                     });
28153             }
28154
28155             function behavior(surface) {
28156                 _done = false;
28157                 _timer = timer(function() {
28158                     // wait for elements to actually become selected
28159                     if (surface.selectAll(selector).empty()) {
28160                         return false;
28161                     }
28162
28163                     surface.call(run, 'from');
28164                     _timer.stop();
28165                     return true;
28166                 }, 20);
28167             }
28168
28169             behavior.restartIfNeeded = function(surface) {
28170                 if (_selected.empty()) {
28171                     surface.call(run, 'from');
28172                     if (_timer) {
28173                         _timer.stop();
28174                     }
28175                 }
28176             };
28177
28178             behavior.off = function() {
28179                 _done = true;
28180                 if (_timer) {
28181                     _timer.stop();
28182                 }
28183                 _selected
28184                     .interrupt()
28185                     .call(reset);
28186             };
28187
28188
28189             return behavior;
28190         }
28191
28192         /* Creates a keybinding behavior for an operation */
28193         function behaviorOperation(context) {
28194             var _operation;
28195
28196             function keypress() {
28197                 // prevent operations during low zoom selection
28198                 if (!context.map().withinEditableZoom()) { return; }
28199
28200                 if (_operation.availableForKeypress && !_operation.availableForKeypress()) { return; }
28201
28202                 event.preventDefault();
28203
28204                 var disabled = _operation.disabled();
28205
28206                 if (disabled) {
28207                     context.ui().flash
28208                         .duration(4000)
28209                         .iconName('#iD-operation-' + _operation.id)
28210                         .iconClass('operation disabled')
28211                         .text(_operation.tooltip)();
28212
28213                 } else {
28214                     context.ui().flash
28215                         .duration(2000)
28216                         .iconName('#iD-operation-' + _operation.id)
28217                         .iconClass('operation')
28218                         .text(_operation.annotation() || _operation.title)();
28219
28220                     if (_operation.point) { _operation.point(null); }
28221                     _operation();
28222                 }
28223             }
28224
28225
28226             function behavior() {
28227                 if (_operation && _operation.available()) {
28228                     context.keybinding()
28229                         .on(_operation.keys, keypress);
28230                 }
28231
28232                 return behavior;
28233             }
28234
28235
28236             behavior.off = function() {
28237                 context.keybinding()
28238                     .off(_operation.keys);
28239             };
28240
28241
28242             behavior.which = function (_) {
28243                 if (!arguments.length) { return _operation; }
28244                 _operation = _;
28245                 return behavior;
28246             };
28247
28248
28249             return behavior;
28250         }
28251
28252         function operationCircularize(context, selectedIDs) {
28253             var _extent;
28254             var _actions = selectedIDs.map(getAction).filter(Boolean);
28255             var _amount = _actions.length === 1 ? 'single' : 'multiple';
28256             var _coords = utilGetAllNodes(selectedIDs, context.graph())
28257                 .map(function(n) { return n.loc; });
28258
28259             function getAction(entityID) {
28260
28261                 var entity = context.entity(entityID);
28262
28263                 if (entity.type !== 'way' || new Set(entity.nodes).size <= 1) { return null; }
28264
28265                 if (!_extent) {
28266                     _extent =  entity.extent(context.graph());
28267                 } else {
28268                     _extent = _extent.extend(entity.extent(context.graph()));
28269                 }
28270
28271                 return actionCircularize(entityID, context.projection);
28272             }
28273
28274             var operation = function() {
28275                 if (!_actions.length) { return; }
28276
28277                 var combinedAction = function(graph, t) {
28278                     _actions.forEach(function(action) {
28279                         if (!action.disabled(graph)) {
28280                             graph = action(graph, t);
28281                         }
28282                     });
28283                     return graph;
28284                 };
28285                 combinedAction.transitionable = true;
28286
28287                 context.perform(combinedAction, operation.annotation());
28288
28289                 window.setTimeout(function() {
28290                     context.validator().validate();
28291                 }, 300);  // after any transition
28292             };
28293
28294
28295             operation.available = function() {
28296                 return _actions.length && selectedIDs.length === _actions.length;
28297             };
28298
28299
28300             // don't cache this because the visible extent could change
28301             operation.disabled = function() {
28302                 if (!_actions.length) { return ''; }
28303
28304                 var actionDisableds = _actions.map(function(action) {
28305                     return action.disabled(context.graph());
28306                 }).filter(Boolean);
28307
28308                 if (actionDisableds.length === _actions.length) {
28309                     // none of the features can be circularized
28310
28311                     if (new Set(actionDisableds).size > 1) {
28312                         return 'multiple_blockers';
28313                     }
28314                     return actionDisableds[0];
28315                 } else if (_extent.percentContainedIn(context.map().extent()) < 0.8) {
28316                     return 'too_large';
28317                 } else if (someMissing()) {
28318                     return 'not_downloaded';
28319                 } else if (selectedIDs.some(context.hasHiddenConnections)) {
28320                     return 'connected_to_hidden';
28321                 }
28322
28323                 return false;
28324
28325
28326                 function someMissing() {
28327                     if (context.inIntro()) { return false; }
28328                     var osm = context.connection();
28329                     if (osm) {
28330                         var missing = _coords.filter(function(loc) { return !osm.isDataLoaded(loc); });
28331                         if (missing.length) {
28332                             missing.forEach(function(loc) { context.loadTileAtLoc(loc); });
28333                             return true;
28334                         }
28335                     }
28336                     return false;
28337                 }
28338             };
28339
28340
28341             operation.tooltip = function() {
28342                 var disable = operation.disabled();
28343                 return disable ?
28344                     _t('operations.circularize.' + disable + '.' + _amount) :
28345                     _t('operations.circularize.description.' + _amount);
28346             };
28347
28348
28349             operation.annotation = function() {
28350                 return _t('operations.circularize.annotation.' + _amount);
28351             };
28352
28353
28354             operation.id = 'circularize';
28355             operation.keys = [_t('operations.circularize.key')];
28356             operation.title = _t('operations.circularize.title');
28357             operation.behavior = behaviorOperation(context).which(operation);
28358
28359             return operation;
28360         }
28361
28362         // Translate a MacOS key command into the appropriate Windows/Linux equivalent.
28363         // For example, ⌘Z -> Ctrl+Z
28364         var uiCmd = function (code) {
28365             var detected = utilDetect();
28366
28367             if (detected.os === 'mac') {
28368                 return code;
28369             }
28370
28371             if (detected.os === 'win') {
28372                 if (code === '⌘⇧Z') { return 'Ctrl+Y'; }
28373             }
28374
28375             var result = '',
28376                 replacements = {
28377                     '⌘': 'Ctrl',
28378                     '⇧': 'Shift',
28379                     '⌥': 'Alt',
28380                     '⌫': 'Backspace',
28381                     '⌦': 'Delete'
28382                 };
28383
28384             for (var i = 0; i < code.length; i++) {
28385                 if (code[i] in replacements) {
28386                     result += replacements[code[i]] + (i < code.length - 1 ? '+' : '');
28387                 } else {
28388                     result += code[i];
28389                 }
28390             }
28391
28392             return result;
28393         };
28394
28395
28396         // return a display-focused string for a given keyboard code
28397         uiCmd.display = function(code) {
28398             if (code.length !== 1) { return code; }
28399
28400             var detected = utilDetect();
28401             var mac = (detected.os === 'mac');
28402             var replacements = {
28403                 '⌘': mac ? '⌘ ' + _t('shortcuts.key.cmd')    : _t('shortcuts.key.ctrl'),
28404                 '⇧': mac ? '⇧ ' + _t('shortcuts.key.shift')  : _t('shortcuts.key.shift'),
28405                 '⌥': mac ? '⌥ ' + _t('shortcuts.key.option') : _t('shortcuts.key.alt'),
28406                 '⌃': mac ? '⌃ ' + _t('shortcuts.key.ctrl')   : _t('shortcuts.key.ctrl'),
28407                 '⌫': mac ? '⌫ ' + _t('shortcuts.key.delete') : _t('shortcuts.key.backspace'),
28408                 '⌦': mac ? '⌦ ' + _t('shortcuts.key.del')    : _t('shortcuts.key.del'),
28409                 '↖': mac ? '↖ ' + _t('shortcuts.key.pgup')   : _t('shortcuts.key.pgup'),
28410                 '↘': mac ? '↘ ' + _t('shortcuts.key.pgdn')   : _t('shortcuts.key.pgdn'),
28411                 '⇞': mac ? '⇞ ' + _t('shortcuts.key.home')   : _t('shortcuts.key.home'),
28412                 '⇟': mac ? '⇟ ' + _t('shortcuts.key.end')    : _t('shortcuts.key.end'),
28413                 '↵': mac ? '⏎ ' + _t('shortcuts.key.return') : _t('shortcuts.key.enter'),
28414                 '⎋': mac ? '⎋ ' + _t('shortcuts.key.esc')    : _t('shortcuts.key.esc'),
28415                 '☰': mac ? '☰ ' + _t('shortcuts.key.menu')  : _t('shortcuts.key.menu'),
28416             };
28417
28418             return replacements[code] || code;
28419         };
28420
28421         function operationDelete(context, selectedIDs) {
28422             var multi = (selectedIDs.length === 1 ? 'single' : 'multiple');
28423             var action = actionDeleteMultiple(selectedIDs);
28424             var nodes = utilGetAllNodes(selectedIDs, context.graph());
28425             var coords = nodes.map(function(n) { return n.loc; });
28426             var extent = utilTotalExtent(selectedIDs, context.graph());
28427
28428
28429             var operation = function() {
28430                 var nextSelectedID;
28431                 var nextSelectedLoc;
28432
28433                 if (selectedIDs.length === 1) {
28434                     var id = selectedIDs[0];
28435                     var entity = context.entity(id);
28436                     var geometry = entity.geometry(context.graph());
28437                     var parents = context.graph().parentWays(entity);
28438                     var parent = parents[0];
28439
28440                     // Select the next closest node in the way.
28441                     if (geometry === 'vertex') {
28442                         var nodes = parent.nodes;
28443                         var i = nodes.indexOf(id);
28444
28445                         if (i === 0) {
28446                             i++;
28447                         } else if (i === nodes.length - 1) {
28448                             i--;
28449                         } else {
28450                             var a = geoSphericalDistance(entity.loc, context.entity(nodes[i - 1]).loc);
28451                             var b = geoSphericalDistance(entity.loc, context.entity(nodes[i + 1]).loc);
28452                             i = a < b ? i - 1 : i + 1;
28453                         }
28454
28455                         nextSelectedID = nodes[i];
28456                         nextSelectedLoc = context.entity(nextSelectedID).loc;
28457                     }
28458                 }
28459
28460                 context.perform(action, operation.annotation());
28461                 context.validator().validate();
28462
28463                 if (nextSelectedID && nextSelectedLoc) {
28464                     if (context.hasEntity(nextSelectedID)) {
28465                         context.enter(modeSelect(context, [nextSelectedID]).follow(true));
28466                     } else {
28467                         context.map().centerEase(nextSelectedLoc);
28468                         context.enter(modeBrowse(context));
28469                     }
28470                 } else {
28471                     context.enter(modeBrowse(context));
28472                 }
28473
28474             };
28475
28476
28477             operation.available = function() {
28478                 return true;
28479             };
28480
28481
28482             operation.disabled = function() {
28483                 if (extent.percentContainedIn(context.map().extent()) < 0.8) {
28484                     return 'too_large';
28485                 } else if (someMissing()) {
28486                     return 'not_downloaded';
28487                 } else if (selectedIDs.some(context.hasHiddenConnections)) {
28488                     return 'connected_to_hidden';
28489                 } else if (selectedIDs.some(protectedMember)) {
28490                     return 'part_of_relation';
28491                 } else if (selectedIDs.some(incompleteRelation)) {
28492                     return 'incomplete_relation';
28493                 } else if (selectedIDs.some(hasWikidataTag)) {
28494                     return 'has_wikidata_tag';
28495                 }
28496
28497                 return false;
28498
28499
28500                 function someMissing() {
28501                     if (context.inIntro()) { return false; }
28502                     var osm = context.connection();
28503                     if (osm) {
28504                         var missing = coords.filter(function(loc) { return !osm.isDataLoaded(loc); });
28505                         if (missing.length) {
28506                             missing.forEach(function(loc) { context.loadTileAtLoc(loc); });
28507                             return true;
28508                         }
28509                     }
28510                     return false;
28511                 }
28512
28513                 function hasWikidataTag(id) {
28514                     var entity = context.entity(id);
28515                     return entity.tags.wikidata && entity.tags.wikidata.trim().length > 0;
28516                 }
28517
28518                 function incompleteRelation(id) {
28519                     var entity = context.entity(id);
28520                     return entity.type === 'relation' && !entity.isComplete(context.graph());
28521                 }
28522
28523                 function protectedMember(id) {
28524                     var entity = context.entity(id);
28525                     if (entity.type !== 'way') { return false; }
28526
28527                     var parents = context.graph().parentRelations(entity);
28528                     for (var i = 0; i < parents.length; i++) {
28529                         var parent = parents[i];
28530                         var type = parent.tags.type;
28531                         var role = parent.memberById(id).role || 'outer';
28532                         if (type === 'route' || type === 'boundary' || (type === 'multipolygon' && role === 'outer')) {
28533                             return true;
28534                         }
28535                     }
28536                     return false;
28537                 }
28538             };
28539
28540
28541             operation.tooltip = function() {
28542                 var disable = operation.disabled();
28543                 return disable ?
28544                     _t('operations.delete.' + disable + '.' + multi) :
28545                     _t('operations.delete.description' + '.' + multi);
28546             };
28547
28548
28549             operation.annotation = function() {
28550                 return selectedIDs.length === 1 ?
28551                     _t('operations.delete.annotation.' + context.graph().geometry(selectedIDs[0])) :
28552                     _t('operations.delete.annotation.multiple', { n: selectedIDs.length });
28553             };
28554
28555
28556             operation.id = 'delete';
28557             operation.keys = [uiCmd('⌘⌫'), uiCmd('⌘⌦'), uiCmd('⌦')];
28558             operation.title = _t('operations.delete.title');
28559             operation.behavior = behaviorOperation(context).which(operation);
28560
28561             return operation;
28562         }
28563
28564         function operationOrthogonalize(context, selectedIDs) {
28565             var _extent;
28566             var _type;
28567             var _actions = selectedIDs.map(chooseAction).filter(Boolean);
28568             var _amount = _actions.length === 1 ? 'single' : 'multiple';
28569             var _coords = utilGetAllNodes(selectedIDs, context.graph())
28570                 .map(function(n) { return n.loc; });
28571
28572
28573             function chooseAction(entityID) {
28574
28575                 var entity = context.entity(entityID);
28576                 var geometry = entity.geometry(context.graph());
28577
28578                 if (!_extent) {
28579                     _extent =  entity.extent(context.graph());
28580                 } else {
28581                     _extent = _extent.extend(entity.extent(context.graph()));
28582                 }
28583
28584                 // square a line/area
28585                 if (entity.type === 'way' && new Set(entity.nodes).size > 2 ) {
28586                     if (_type && _type !== 'feature') { return null; }
28587                     _type = 'feature';
28588                     return actionOrthogonalize(entityID, context.projection);
28589
28590                 // square a single vertex
28591                 } else if (geometry === 'vertex') {
28592                     if (_type && _type !== 'corner') { return null; }
28593                     _type = 'corner';
28594                     var graph = context.graph();
28595                     var parents = graph.parentWays(entity);
28596                     if (parents.length === 1) {
28597                         var way = parents[0];
28598                         if (way.nodes.indexOf(entityID) !== -1) {
28599                             return actionOrthogonalize(way.id, context.projection, entityID);
28600                         }
28601                     }
28602                 }
28603
28604                 return null;
28605             }
28606
28607
28608             var operation = function() {
28609                 if (!_actions.length) { return; }
28610
28611                 var combinedAction = function(graph, t) {
28612                     _actions.forEach(function(action) {
28613                         if (!action.disabled(graph)) {
28614                             graph = action(graph, t);
28615                         }
28616                     });
28617                     return graph;
28618                 };
28619                 combinedAction.transitionable = true;
28620
28621                 context.perform(combinedAction, operation.annotation());
28622
28623                 window.setTimeout(function() {
28624                     context.validator().validate();
28625                 }, 300);  // after any transition
28626             };
28627
28628
28629             operation.available = function() {
28630                 return _actions.length && selectedIDs.length === _actions.length;
28631             };
28632
28633
28634             // don't cache this because the visible extent could change
28635             operation.disabled = function() {
28636                 if (!_actions.length) { return ''; }
28637
28638                 var actionDisableds = _actions.map(function(action) {
28639                     return action.disabled(context.graph());
28640                 }).filter(Boolean);
28641
28642                 if (actionDisableds.length === _actions.length) {
28643                     // none of the features can be squared
28644
28645                     if (new Set(actionDisableds).size > 1) {
28646                         return 'multiple_blockers';
28647                     }
28648                     return actionDisableds[0];
28649                 } else if (_extent &&
28650                            _extent.percentContainedIn(context.map().extent()) < 0.8) {
28651                     return 'too_large';
28652                 } else if (someMissing()) {
28653                     return 'not_downloaded';
28654                 } else if (selectedIDs.some(context.hasHiddenConnections)) {
28655                     return 'connected_to_hidden';
28656                 }
28657
28658                 return false;
28659
28660
28661                 function someMissing() {
28662                     if (context.inIntro()) { return false; }
28663                     var osm = context.connection();
28664                     if (osm) {
28665                         var missing = _coords.filter(function(loc) { return !osm.isDataLoaded(loc); });
28666                         if (missing.length) {
28667                             missing.forEach(function(loc) { context.loadTileAtLoc(loc); });
28668                             return true;
28669                         }
28670                     }
28671                     return false;
28672                 }
28673             };
28674
28675
28676             operation.tooltip = function() {
28677                 var disable = operation.disabled();
28678                 return disable ?
28679                     _t('operations.orthogonalize.' + disable + '.' + _amount) :
28680                     _t('operations.orthogonalize.description.' + _type + '.' + _amount);
28681             };
28682
28683
28684             operation.annotation = function() {
28685                 return _t('operations.orthogonalize.annotation.' + _type + '.' + _amount);
28686             };
28687
28688
28689             operation.id = 'orthogonalize';
28690             operation.keys = [_t('operations.orthogonalize.key')];
28691             operation.title = _t('operations.orthogonalize.title');
28692             operation.behavior = behaviorOperation(context).which(operation);
28693
28694             return operation;
28695         }
28696
28697         function operationReflectShort(context, selectedIDs) {
28698             return operationReflect(context, selectedIDs, 'short');
28699         }
28700
28701
28702         function operationReflectLong(context, selectedIDs) {
28703             return operationReflect(context, selectedIDs, 'long');
28704         }
28705
28706
28707         function operationReflect(context, selectedIDs, axis) {
28708             axis = axis || 'long';
28709             var multi = (selectedIDs.length === 1 ? 'single' : 'multiple');
28710             var nodes = utilGetAllNodes(selectedIDs, context.graph());
28711             var coords = nodes.map(function(n) { return n.loc; });
28712             var extent = utilTotalExtent(selectedIDs, context.graph());
28713
28714
28715             var operation = function() {
28716                 var action = actionReflect(selectedIDs, context.projection)
28717                     .useLongAxis(Boolean(axis === 'long'));
28718
28719                 context.perform(action, operation.annotation());
28720
28721                 window.setTimeout(function() {
28722                     context.validator().validate();
28723                 }, 300);  // after any transition
28724             };
28725
28726
28727             operation.available = function() {
28728                 return nodes.length >= 3;
28729             };
28730
28731
28732             // don't cache this because the visible extent could change
28733             operation.disabled = function() {
28734                 if (extent.percentContainedIn(context.map().extent()) < 0.8) {
28735                     return 'too_large';
28736                 } else if (someMissing()) {
28737                     return 'not_downloaded';
28738                 } else if (selectedIDs.some(context.hasHiddenConnections)) {
28739                     return 'connected_to_hidden';
28740                 } else if (selectedIDs.some(incompleteRelation)) {
28741                     return 'incomplete_relation';
28742                 }
28743
28744                 return false;
28745
28746
28747                 function someMissing() {
28748                     if (context.inIntro()) { return false; }
28749                     var osm = context.connection();
28750                     if (osm) {
28751                         var missing = coords.filter(function(loc) { return !osm.isDataLoaded(loc); });
28752                         if (missing.length) {
28753                             missing.forEach(function(loc) { context.loadTileAtLoc(loc); });
28754                             return true;
28755                         }
28756                     }
28757                     return false;
28758                 }
28759
28760                 function incompleteRelation(id) {
28761                     var entity = context.entity(id);
28762                     return entity.type === 'relation' && !entity.isComplete(context.graph());
28763                 }
28764             };
28765
28766
28767             operation.tooltip = function() {
28768                 var disable = operation.disabled();
28769                 return disable ?
28770                     _t('operations.reflect.' + disable + '.' + multi) :
28771                     _t('operations.reflect.description.' + axis + '.' + multi);
28772             };
28773
28774
28775             operation.annotation = function() {
28776                 return _t('operations.reflect.annotation.' + axis + '.' + multi);
28777             };
28778
28779
28780             operation.id = 'reflect-' + axis;
28781             operation.keys = [_t('operations.reflect.key.' + axis)];
28782             operation.title = _t('operations.reflect.title.' + axis);
28783             operation.behavior = behaviorOperation(context).which(operation);
28784
28785             return operation;
28786         }
28787
28788         function operationMove(context, selectedIDs) {
28789             var multi = (selectedIDs.length === 1 ? 'single' : 'multiple');
28790             var nodes = utilGetAllNodes(selectedIDs, context.graph());
28791             var coords = nodes.map(function(n) { return n.loc; });
28792             var extent = utilTotalExtent(selectedIDs, context.graph());
28793
28794
28795             var operation = function() {
28796                 context.enter(modeMove(context, selectedIDs));
28797             };
28798
28799
28800             operation.available = function() {
28801                 return selectedIDs.length > 1 ||
28802                     context.entity(selectedIDs[0]).type !== 'node';
28803             };
28804
28805
28806             operation.disabled = function() {
28807                 if (extent.percentContainedIn(context.map().extent()) < 0.8) {
28808                     return 'too_large';
28809                 } else if (someMissing()) {
28810                     return 'not_downloaded';
28811                 } else if (selectedIDs.some(context.hasHiddenConnections)) {
28812                     return 'connected_to_hidden';
28813                 } else if (selectedIDs.some(incompleteRelation)) {
28814                     return 'incomplete_relation';
28815                 }
28816
28817                 return false;
28818
28819
28820                 function someMissing() {
28821                     if (context.inIntro()) { return false; }
28822                     var osm = context.connection();
28823                     if (osm) {
28824                         var missing = coords.filter(function(loc) { return !osm.isDataLoaded(loc); });
28825                         if (missing.length) {
28826                             missing.forEach(function(loc) { context.loadTileAtLoc(loc); });
28827                             return true;
28828                         }
28829                     }
28830                     return false;
28831                 }
28832
28833                 function incompleteRelation(id) {
28834                     var entity = context.entity(id);
28835                     return entity.type === 'relation' && !entity.isComplete(context.graph());
28836                 }
28837             };
28838
28839
28840             operation.tooltip = function() {
28841                 var disable = operation.disabled();
28842                 return disable ?
28843                     _t('operations.move.' + disable + '.' + multi) :
28844                     _t('operations.move.description.' + multi);
28845             };
28846
28847
28848             operation.annotation = function() {
28849                 return selectedIDs.length === 1 ?
28850                     _t('operations.move.annotation.' + context.graph().geometry(selectedIDs[0])) :
28851                     _t('operations.move.annotation.multiple');
28852             };
28853
28854
28855             operation.id = 'move';
28856             operation.keys = [_t('operations.move.key')];
28857             operation.title = _t('operations.move.title');
28858             operation.behavior = behaviorOperation(context).which(operation);
28859
28860             operation.mouseOnly = true;
28861
28862             return operation;
28863         }
28864
28865         function modeRotate(context, entityIDs) {
28866             var mode = {
28867                 id: 'rotate',
28868                 button: 'browse'
28869             };
28870
28871             var keybinding = utilKeybinding('rotate');
28872             var behaviors = [
28873                 behaviorEdit(context),
28874                 operationCircularize(context, entityIDs).behavior,
28875                 operationDelete(context, entityIDs).behavior,
28876                 operationMove(context, entityIDs).behavior,
28877                 operationOrthogonalize(context, entityIDs).behavior,
28878                 operationReflectLong(context, entityIDs).behavior,
28879                 operationReflectShort(context, entityIDs).behavior
28880             ];
28881             var annotation = entityIDs.length === 1 ?
28882                 _t('operations.rotate.annotation.' + context.graph().geometry(entityIDs[0])) :
28883                 _t('operations.rotate.annotation.multiple');
28884
28885             var _prevGraph;
28886             var _prevAngle;
28887             var _prevTransform;
28888             var _pivot;
28889
28890
28891             function doRotate() {
28892                 var fn;
28893                 if (context.graph() !== _prevGraph) {
28894                     fn = context.perform;
28895                 } else {
28896                     fn = context.replace;
28897                 }
28898
28899                 // projection changed, recalculate _pivot
28900                 var projection = context.projection;
28901                 var currTransform = projection.transform();
28902                 if (!_prevTransform ||
28903                     currTransform.k !== _prevTransform.k ||
28904                     currTransform.x !== _prevTransform.x ||
28905                     currTransform.y !== _prevTransform.y) {
28906
28907                     var nodes = utilGetAllNodes(entityIDs, context.graph());
28908                     var points = nodes.map(function(n) { return projection(n.loc); });
28909                     _pivot = getPivot(points);
28910                     _prevAngle = undefined;
28911                 }
28912
28913
28914                 var currMouse = context.map().mouse();
28915                 var currAngle = Math.atan2(currMouse[1] - _pivot[1], currMouse[0] - _pivot[0]);
28916
28917                 if (typeof _prevAngle === 'undefined') { _prevAngle = currAngle; }
28918                 var delta = currAngle - _prevAngle;
28919
28920                 fn(actionRotate(entityIDs, _pivot, delta, projection));
28921
28922                 _prevTransform = currTransform;
28923                 _prevAngle = currAngle;
28924                 _prevGraph = context.graph();
28925             }
28926
28927             function getPivot(points) {
28928                 var _pivot;
28929                 if (points.length === 1) {
28930                     _pivot = points[0];
28931                 } else if (points.length === 2) {
28932                     _pivot = geoVecInterp(points[0], points[1], 0.5);
28933                 } else {
28934                     var polygonHull = d3_polygonHull(points);
28935                     if (polygonHull.length === 2) {
28936                         _pivot = geoVecInterp(points[0], points[1], 0.5);
28937                     } else {
28938                         _pivot = d3_polygonCentroid(d3_polygonHull(points));
28939                     }
28940                 }
28941                 return _pivot;
28942             }
28943
28944
28945             function finish() {
28946                 event.stopPropagation();
28947                 context.replace(actionNoop(), annotation);
28948                 context.enter(modeSelect(context, entityIDs));
28949             }
28950
28951
28952             function cancel() {
28953                 context.pop();
28954                 context.enter(modeSelect(context, entityIDs));
28955             }
28956
28957
28958             function undone() {
28959                 context.enter(modeBrowse(context));
28960             }
28961
28962
28963             mode.enter = function() {
28964                 context.features().forceVisible(entityIDs);
28965
28966                 behaviors.forEach(context.install);
28967
28968                 context.surface()
28969                     .on('mousemove.rotate', doRotate)
28970                     .on('click.rotate', finish);
28971
28972                 context.history()
28973                     .on('undone.rotate', undone);
28974
28975                 keybinding
28976                     .on('⎋', cancel)
28977                     .on('↩', finish);
28978
28979                 select(document)
28980                     .call(keybinding);
28981             };
28982
28983
28984             mode.exit = function() {
28985                 behaviors.forEach(context.uninstall);
28986
28987                 context.surface()
28988                     .on('mousemove.rotate', null)
28989                     .on('click.rotate', null);
28990
28991                 context.history()
28992                     .on('undone.rotate', null);
28993
28994                 select(document)
28995                     .call(keybinding.unbind);
28996
28997                 context.features().forceVisible([]);
28998             };
28999
29000
29001             mode.selectedIDs = function() {
29002                 if (!arguments.length) { return entityIDs; }
29003                 // no assign
29004                 return mode;
29005             };
29006
29007
29008             return mode;
29009         }
29010
29011         function operationRotate(context, selectedIDs) {
29012             var multi = (selectedIDs.length === 1 ? 'single' : 'multiple');
29013             var nodes = utilGetAllNodes(selectedIDs, context.graph());
29014             var coords = nodes.map(function(n) { return n.loc; });
29015             var extent = utilTotalExtent(selectedIDs, context.graph());
29016
29017
29018             var operation = function() {
29019                 context.enter(modeRotate(context, selectedIDs));
29020             };
29021
29022
29023             operation.available = function() {
29024                 return nodes.length >= 2;
29025             };
29026
29027
29028             operation.disabled = function() {
29029
29030                 if (extent.percentContainedIn(context.map().extent()) < 0.8) {
29031                     return 'too_large';
29032                 } else if (someMissing()) {
29033                     return 'not_downloaded';
29034                 } else if (selectedIDs.some(context.hasHiddenConnections)) {
29035                     return 'connected_to_hidden';
29036                 } else if (selectedIDs.some(incompleteRelation)) {
29037                     return 'incomplete_relation';
29038                 }
29039
29040                 return false;
29041
29042
29043                 function someMissing() {
29044                     if (context.inIntro()) { return false; }
29045                     var osm = context.connection();
29046                     if (osm) {
29047                         var missing = coords.filter(function(loc) { return !osm.isDataLoaded(loc); });
29048                         if (missing.length) {
29049                             missing.forEach(function(loc) { context.loadTileAtLoc(loc); });
29050                             return true;
29051                         }
29052                     }
29053                     return false;
29054                 }
29055
29056                 function incompleteRelation(id) {
29057                     var entity = context.entity(id);
29058                     return entity.type === 'relation' && !entity.isComplete(context.graph());
29059                 }
29060             };
29061
29062
29063             operation.tooltip = function() {
29064                 var disable = operation.disabled();
29065                 return disable ?
29066                     _t('operations.rotate.' + disable + '.' + multi) :
29067                     _t('operations.rotate.description.' + multi);
29068             };
29069
29070
29071             operation.annotation = function() {
29072                 return selectedIDs.length === 1 ?
29073                     _t('operations.rotate.annotation.' + context.graph().geometry(selectedIDs[0])) :
29074                     _t('operations.rotate.annotation.multiple');
29075             };
29076
29077
29078             operation.id = 'rotate';
29079             operation.keys = [_t('operations.rotate.key')];
29080             operation.title = _t('operations.rotate.title');
29081             operation.behavior = behaviorOperation(context).which(operation);
29082
29083             operation.mouseOnly = true;
29084
29085             return operation;
29086         }
29087
29088         function modeMove(context, entityIDs, baseGraph) {
29089             var mode = {
29090                 id: 'move',
29091                 button: 'browse'
29092             };
29093
29094             var keybinding = utilKeybinding('move');
29095             var behaviors = [
29096                 behaviorEdit(context),
29097                 operationCircularize(context, entityIDs).behavior,
29098                 operationDelete(context, entityIDs).behavior,
29099                 operationOrthogonalize(context, entityIDs).behavior,
29100                 operationReflectLong(context, entityIDs).behavior,
29101                 operationReflectShort(context, entityIDs).behavior,
29102                 operationRotate(context, entityIDs).behavior
29103             ];
29104             var annotation = entityIDs.length === 1 ?
29105                 _t('operations.move.annotation.' + context.graph().geometry(entityIDs[0])) :
29106                 _t('operations.move.annotation.multiple');
29107
29108             var _prevGraph;
29109             var _cache;
29110             var _origin;
29111             var _nudgeInterval;
29112
29113
29114             function doMove(nudge) {
29115                 nudge = nudge || [0, 0];
29116
29117                 var fn;
29118                 if (_prevGraph !== context.graph()) {
29119                     _cache = {};
29120                     _origin = context.map().mouseCoordinates();
29121                     fn = context.perform;
29122                 } else {
29123                     fn = context.overwrite;
29124                 }
29125
29126                 var currMouse = context.map().mouse();
29127                 var origMouse = context.projection(_origin);
29128                 var delta = geoVecSubtract(geoVecSubtract(currMouse, origMouse), nudge);
29129
29130                 fn(actionMove(entityIDs, delta, context.projection, _cache));
29131                 _prevGraph = context.graph();
29132             }
29133
29134
29135             function startNudge(nudge) {
29136                 if (_nudgeInterval) { window.clearInterval(_nudgeInterval); }
29137                 _nudgeInterval = window.setInterval(function() {
29138                     context.map().pan(nudge);
29139                     doMove(nudge);
29140                 }, 50);
29141             }
29142
29143
29144             function stopNudge() {
29145                 if (_nudgeInterval) {
29146                     window.clearInterval(_nudgeInterval);
29147                     _nudgeInterval = null;
29148                 }
29149             }
29150
29151
29152             function move() {
29153                 doMove();
29154                 var nudge = geoViewportEdge(context.map().mouse(), context.map().dimensions());
29155                 if (nudge) {
29156                     startNudge(nudge);
29157                 } else {
29158                     stopNudge();
29159                 }
29160             }
29161
29162
29163             function finish() {
29164                 event.stopPropagation();
29165                 context.replace(actionNoop(), annotation);
29166                 context.enter(modeSelect(context, entityIDs));
29167                 stopNudge();
29168             }
29169
29170
29171             function cancel() {
29172                 if (baseGraph) {
29173                     while (context.graph() !== baseGraph) { context.pop(); }
29174                     context.enter(modeBrowse(context));
29175                 } else {
29176                     context.pop();
29177                     context.enter(modeSelect(context, entityIDs));
29178                 }
29179                 stopNudge();
29180             }
29181
29182
29183             function undone() {
29184                 context.enter(modeBrowse(context));
29185             }
29186
29187
29188             mode.enter = function() {
29189                 _origin = context.map().mouseCoordinates();
29190                 _prevGraph = null;
29191                 _cache = {};
29192
29193                 context.features().forceVisible(entityIDs);
29194
29195                 behaviors.forEach(context.install);
29196
29197                 context.surface()
29198                     .on('mousemove.move', move)
29199                     .on('click.move', finish);
29200
29201                 context.history()
29202                     .on('undone.move', undone);
29203
29204                 keybinding
29205                     .on('⎋', cancel)
29206                     .on('↩', finish);
29207
29208                 select(document)
29209                     .call(keybinding);
29210             };
29211
29212
29213             mode.exit = function() {
29214                 stopNudge();
29215
29216                 behaviors.forEach(function(behavior) {
29217                     context.uninstall(behavior);
29218                 });
29219
29220                 context.surface()
29221                     .on('mousemove.move', null)
29222                     .on('click.move', null);
29223
29224                 context.history()
29225                     .on('undone.move', null);
29226
29227                 select(document)
29228                     .call(keybinding.unbind);
29229
29230                 context.features().forceVisible([]);
29231             };
29232
29233
29234             mode.selectedIDs = function() {
29235                 if (!arguments.length) { return entityIDs; }
29236                 // no assign
29237                 return mode;
29238             };
29239
29240
29241             return mode;
29242         }
29243
29244         // see also `operationPaste`
29245         function behaviorPaste(context) {
29246
29247             function doPaste() {
29248                 // prevent paste during low zoom selection
29249                 if (!context.map().withinEditableZoom()) { return; }
29250
29251                 event.preventDefault();
29252
29253                 var baseGraph = context.graph();
29254                 var mouse = context.map().mouse();
29255                 var projection = context.projection;
29256                 var viewport = geoExtent(projection.clipExtent()).polygon();
29257
29258                 if (!geoPointInPolygon(mouse, viewport)) { return; }
29259
29260                 var oldIDs = context.copyIDs();
29261                 if (!oldIDs.length) { return; }
29262
29263                 var extent = geoExtent();
29264                 var oldGraph = context.copyGraph();
29265                 var newIDs = [];
29266
29267                 var action = actionCopyEntities(oldIDs, oldGraph);
29268                 context.perform(action);
29269
29270                 var copies = action.copies();
29271                 var originals = new Set();
29272                 Object.values(copies).forEach(function(entity) { originals.add(entity.id); });
29273
29274                 for (var id in copies) {
29275                     var oldEntity = oldGraph.entity(id);
29276                     var newEntity = copies[id];
29277
29278                     extent._extend(oldEntity.extent(oldGraph));
29279
29280                     // Exclude child nodes from newIDs if their parent way was also copied.
29281                     var parents = context.graph().parentWays(newEntity);
29282                     var parentCopied = parents.some(function(parent) {
29283                         return originals.has(parent.id);
29284                     });
29285
29286                     if (!parentCopied) {
29287                         newIDs.push(newEntity.id);
29288                     }
29289                 }
29290
29291                 // Put pasted objects where mouse pointer is..
29292                 var copyPoint = (context.copyLonLat() && projection(context.copyLonLat())) || projection(extent.center());
29293                 var delta = geoVecSubtract(mouse, copyPoint);
29294
29295                 context.perform(actionMove(newIDs, delta, projection));
29296                 context.enter(modeMove(context, newIDs, baseGraph));
29297             }
29298
29299
29300             function behavior() {
29301                 context.keybinding().on(uiCmd('⌘V'), doPaste);
29302                 return behavior;
29303             }
29304
29305
29306             behavior.off = function() {
29307                 context.keybinding().off(uiCmd('⌘V'));
29308             };
29309
29310
29311             return behavior;
29312         }
29313
29314         /*
29315             `behaviorDrag` is like `d3_behavior.drag`, with the following differences:
29316
29317             * The `origin` function is expected to return an [x, y] tuple rather than an
29318               {x, y} object.
29319             * The events are `start`, `move`, and `end`.
29320               (https://github.com/mbostock/d3/issues/563)
29321             * The `start` event is not dispatched until the first cursor movement occurs.
29322               (https://github.com/mbostock/d3/pull/368)
29323             * The `move` event has a `point` and `delta` [x, y] tuple properties rather
29324               than `x`, `y`, `dx`, and `dy` properties.
29325             * The `end` event is not dispatched if no movement occurs.
29326             * An `off` function is available that unbinds the drag's internal event handlers.
29327          */
29328
29329         function behaviorDrag() {
29330             var dispatch$1 = dispatch('start', 'move', 'end');
29331
29332             // see also behaviorSelect
29333             var _tolerancePx = 1; // keep this low to facilitate pixel-perfect micromapping
29334             var _penTolerancePx = 4; // styluses can be touchy so require greater movement - #1981
29335
29336             var _origin = null;
29337             var _selector = '';
29338             var _event;
29339             var _target;
29340             var _surface;
29341             var _pointerId;
29342
29343             // use pointer events on supported platforms; fallback to mouse events
29344             var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
29345
29346             var d3_event_userSelectProperty = utilPrefixCSSProperty('UserSelect');
29347             var d3_event_userSelectSuppress = function() {
29348                     var selection$1 = selection();
29349                     var select = selection$1.style(d3_event_userSelectProperty);
29350                     selection$1.style(d3_event_userSelectProperty, 'none');
29351                     return function() {
29352                         selection$1.style(d3_event_userSelectProperty, select);
29353                     };
29354                 };
29355
29356
29357             function eventOf(thiz, argumentz) {
29358                 return function(e1) {
29359                     e1.target = behavior;
29360                     customEvent(e1, dispatch$1.apply, dispatch$1, [e1.type, thiz, argumentz]);
29361                 };
29362             }
29363
29364
29365             function pointerdown() {
29366
29367                 if (_pointerId) { return; }
29368
29369                 _pointerId = event.pointerId || 'mouse';
29370
29371                 _target = this;
29372                 _event = eventOf(_target, arguments);
29373
29374                 // only force reflow once per drag
29375                 var pointerLocGetter = utilFastMouse(_surface || _target.parentNode);
29376
29377                 var offset;
29378                 var startOrigin = pointerLocGetter(event);
29379                 var started = false;
29380                 var selectEnable = d3_event_userSelectSuppress();
29381
29382                 select(window)
29383                     .on(_pointerPrefix + 'move.drag', pointermove)
29384                     .on(_pointerPrefix + 'up.drag pointercancel.drag', pointerup, true);
29385
29386                 if (_origin) {
29387                     offset = _origin.apply(_target, arguments);
29388                     offset = [offset[0] - startOrigin[0], offset[1] - startOrigin[1]];
29389                 } else {
29390                     offset = [0, 0];
29391                 }
29392
29393                 event.stopPropagation();
29394
29395
29396                 function pointermove() {
29397                     if (_pointerId !== (event.pointerId || 'mouse')) { return; }
29398
29399                     var p = pointerLocGetter(event);
29400
29401                     if (!started) {
29402                         var dist = geoVecLength(startOrigin,  p);
29403                         var tolerance = event.pointerType === 'pen' ? _penTolerancePx : _tolerancePx;
29404                         // don't start until the drag has actually moved somewhat
29405                         if (dist < tolerance) { return; }
29406
29407                         started = true;
29408                         _event({ type: 'start' });
29409
29410                     // Don't send a `move` event in the same cycle as `start` since dragging
29411                     // a midpoint will convert the target to a node.
29412                     } else {
29413
29414                         startOrigin = p;
29415                         event.stopPropagation();
29416                         event.preventDefault();
29417
29418                         var dx = p[0] - startOrigin[0];
29419                         var dy = p[1] - startOrigin[1];
29420                         _event({
29421                             type: 'move',
29422                             point: [p[0] + offset[0],  p[1] + offset[1]],
29423                             delta: [dx, dy]
29424                         });
29425                     }
29426                 }
29427
29428
29429                 function pointerup() {
29430                     if (_pointerId !== (event.pointerId || 'mouse')) { return; }
29431
29432                     _pointerId = null;
29433
29434                     if (started) {
29435                         _event({ type: 'end' });
29436
29437                         event.preventDefault();
29438                     }
29439
29440                     select(window)
29441                         .on(_pointerPrefix + 'move.drag', null)
29442                         .on(_pointerPrefix + 'up.drag pointercancel.drag', null);
29443
29444                     selectEnable();
29445                 }
29446             }
29447
29448
29449             function behavior(selection) {
29450                 var matchesSelector = utilPrefixDOMProperty('matchesSelector');
29451                 var delegate = pointerdown;
29452
29453                 if (_selector) {
29454                     delegate = function() {
29455                         var root = this;
29456                         var target = event.target;
29457                         for (; target && target !== root; target = target.parentNode) {
29458                             var datum = target.__data__;
29459
29460                             var entity = datum instanceof osmNote ? datum
29461                                 : datum && datum.properties && datum.properties.entity;
29462
29463                             if (entity && target[matchesSelector](_selector)) {
29464                                 return pointerdown.call(target, entity);
29465                             }
29466                         }
29467                     };
29468                 }
29469
29470                 selection
29471                     .on(_pointerPrefix + 'down.drag' + _selector, delegate);
29472             }
29473
29474
29475             behavior.off = function(selection) {
29476                 selection
29477                     .on(_pointerPrefix + 'down.drag' + _selector, null);
29478             };
29479
29480
29481             behavior.selector = function(_) {
29482                 if (!arguments.length) { return _selector; }
29483                 _selector = _;
29484                 return behavior;
29485             };
29486
29487
29488             behavior.origin = function(_) {
29489                 if (!arguments.length) { return _origin; }
29490                 _origin = _;
29491                 return behavior;
29492             };
29493
29494
29495             behavior.cancel = function() {
29496                 select(window)
29497                     .on(_pointerPrefix + 'move.drag', null)
29498                     .on(_pointerPrefix + 'up.drag pointercancel.drag', null);
29499                 return behavior;
29500             };
29501
29502
29503             behavior.target = function() {
29504                 if (!arguments.length) { return _target; }
29505                 _target = arguments[0];
29506                 _event = eventOf(_target, Array.prototype.slice.call(arguments, 1));
29507                 return behavior;
29508             };
29509
29510
29511             behavior.surface = function() {
29512                 if (!arguments.length) { return _surface; }
29513                 _surface = arguments[0];
29514                 return behavior;
29515             };
29516
29517
29518             return utilRebind(behavior, dispatch$1, 'on');
29519         }
29520
29521         function modeDragNode(context) {
29522             var mode = {
29523                 id: 'drag-node',
29524                 button: 'browse'
29525             };
29526             var hover = behaviorHover(context).altDisables(true)
29527                 .on('hover', context.ui().sidebar.hover);
29528             var edit = behaviorEdit(context);
29529
29530             var _nudgeInterval;
29531             var _restoreSelectedIDs = [];
29532             var _wasMidpoint = false;
29533             var _isCancelled = false;
29534             var _activeEntity;
29535             var _startLoc;
29536             var _lastLoc;
29537
29538
29539             function startNudge(entity, nudge) {
29540                 if (_nudgeInterval) { window.clearInterval(_nudgeInterval); }
29541                 _nudgeInterval = window.setInterval(function() {
29542                     context.map().pan(nudge);
29543                     doMove(entity, nudge);
29544                 }, 50);
29545             }
29546
29547
29548             function stopNudge() {
29549                 if (_nudgeInterval) {
29550                     window.clearInterval(_nudgeInterval);
29551                     _nudgeInterval = null;
29552                 }
29553             }
29554
29555
29556             function moveAnnotation(entity) {
29557                 return _t('operations.move.annotation.' + entity.geometry(context.graph()));
29558             }
29559
29560
29561             function connectAnnotation(nodeEntity, targetEntity) {
29562                 var nodeGeometry = nodeEntity.geometry(context.graph());
29563                 var targetGeometry = targetEntity.geometry(context.graph());
29564                 if (nodeGeometry === 'vertex' && targetGeometry === 'vertex') {
29565                     var nodeParentWayIDs = context.graph().parentWays(nodeEntity);
29566                     var targetParentWayIDs = context.graph().parentWays(targetEntity);
29567                     var sharedParentWays = utilArrayIntersection(nodeParentWayIDs, targetParentWayIDs);
29568                     // if both vertices are part of the same way
29569                     if (sharedParentWays.length !== 0) {
29570                         // if the nodes are next to each other, they are merged
29571                         if (sharedParentWays[0].areAdjacent(nodeEntity.id, targetEntity.id)) {
29572                             return _t('operations.connect.annotation.from_vertex.to_adjacent_vertex');
29573                         }
29574                         return _t('operations.connect.annotation.from_vertex.to_sibling_vertex');
29575                     }
29576                 }
29577                 return _t('operations.connect.annotation.from_' + nodeGeometry + '.to_' + targetGeometry);
29578             }
29579
29580
29581             function shouldSnapToNode(target) {
29582                 if (!_activeEntity) { return false; }
29583                 return _activeEntity.geometry(context.graph()) !== 'vertex' ||
29584                     (target.geometry(context.graph()) === 'vertex' || _mainPresetIndex.allowsVertex(target, context.graph()));
29585             }
29586
29587
29588             function origin(entity) {
29589                 return context.projection(entity.loc);
29590             }
29591
29592
29593             function keydown() {
29594                 if (event.keyCode === utilKeybinding.modifierCodes.alt) {
29595                     if (context.surface().classed('nope')) {
29596                         context.surface()
29597                             .classed('nope-suppressed', true);
29598                     }
29599                     context.surface()
29600                         .classed('nope', false)
29601                         .classed('nope-disabled', true);
29602                 }
29603             }
29604
29605
29606             function keyup() {
29607                 if (event.keyCode === utilKeybinding.modifierCodes.alt) {
29608                     if (context.surface().classed('nope-suppressed')) {
29609                         context.surface()
29610                             .classed('nope', true);
29611                     }
29612                     context.surface()
29613                         .classed('nope-suppressed', false)
29614                         .classed('nope-disabled', false);
29615                 }
29616             }
29617
29618
29619             function start(entity) {
29620                 _wasMidpoint = entity.type === 'midpoint';
29621                 var hasHidden = context.features().hasHiddenConnections(entity, context.graph());
29622                 _isCancelled = !context.editable() || event.sourceEvent.shiftKey || hasHidden;
29623
29624
29625                 if (_isCancelled) {
29626                     if (hasHidden) {
29627                         context.ui().flash
29628                             .duration(4000)
29629                             .text(_t('modes.drag_node.connected_to_hidden'))();
29630                     }
29631                     return drag.cancel();
29632                 }
29633
29634                 if (_wasMidpoint) {
29635                     var midpoint = entity;
29636                     entity = osmNode();
29637                     context.perform(actionAddMidpoint(midpoint, entity));
29638                     entity = context.entity(entity.id);   // get post-action entity
29639
29640                     var vertex = context.surface().selectAll('.' + entity.id);
29641                     drag.target(vertex.node(), entity);
29642
29643                 } else {
29644                     context.perform(actionNoop());
29645                 }
29646
29647                 _activeEntity = entity;
29648                 _startLoc = entity.loc;
29649
29650                 hover.ignoreVertex(entity.geometry(context.graph()) === 'vertex');
29651
29652                 context.surface().selectAll('.' + _activeEntity.id)
29653                     .classed('active', true);
29654
29655                 context.enter(mode);
29656             }
29657
29658
29659             // related code
29660             // - `behavior/draw.js` `datum()`
29661             function datum() {
29662                 var event$1 = event && event.sourceEvent;
29663                 if (!event$1 || event$1.altKey) {
29664                     return {};
29665                 } else {
29666                     // When dragging, snap only to touch targets..
29667                     // (this excludes area fills and active drawing elements)
29668                     var d = event$1.target.__data__;
29669                     return (d && d.properties && d.properties.target) ? d : {};
29670                 }
29671             }
29672
29673
29674             function doMove(entity, nudge) {
29675                 nudge = nudge || [0, 0];
29676
29677                 var currPoint = (event && event.point) || context.projection(_lastLoc);
29678                 var currMouse = geoVecSubtract(currPoint, nudge);
29679                 var loc = context.projection.invert(currMouse);
29680
29681                 if (!_nudgeInterval) {   // If not nudging at the edge of the viewport, try to snap..
29682                     // related code
29683                     // - `mode/drag_node.js`     `doMove()`
29684                     // - `behavior/draw.js`      `click()`
29685                     // - `behavior/draw_way.js`  `move()`
29686                     var d = datum();
29687                     var target = d && d.properties && d.properties.entity;
29688                     var targetLoc = target && target.loc;
29689                     var targetNodes = d && d.properties && d.properties.nodes;
29690                     var edge;
29691
29692                     if (targetLoc) {   // snap to node/vertex - a point target with `.loc`
29693                         if (shouldSnapToNode(target)) {
29694                             loc = targetLoc;
29695                         }
29696
29697                     } else if (targetNodes) {   // snap to way - a line target with `.nodes`
29698                         edge = geoChooseEdge(targetNodes, context.map().mouse(), context.projection, end.id);
29699                         if (edge) {
29700                             loc = edge.loc;
29701                         }
29702                     }
29703                 }
29704
29705                 context.replace(
29706                     actionMoveNode(entity.id, loc)
29707                 );
29708
29709                 // Below here: validations
29710                 var isInvalid = false;
29711
29712                 // Check if this connection to `target` could cause relations to break..
29713                 if (target) {
29714                     isInvalid = hasRelationConflict(entity, target, edge, context.graph());
29715                 }
29716
29717                 // Check if this drag causes the geometry to break..
29718                 if (!isInvalid) {
29719                     isInvalid = hasInvalidGeometry(entity, context.graph());
29720                 }
29721
29722
29723                 var nope = context.surface().classed('nope');
29724                 if (isInvalid === 'relation' || isInvalid === 'restriction') {
29725                     if (!nope) {   // about to nope - show hint
29726                         context.ui().flash
29727                             .duration(4000)
29728                             .text(_t('operations.connect.' + isInvalid,
29729                                 { relation: _mainPresetIndex.item('type/restriction').name() }
29730                             ))();
29731                     }
29732                 } else if (isInvalid) {
29733                     var errorID = isInvalid === 'line' ? 'lines' : 'areas';
29734                     context.ui().flash
29735                         .duration(3000)
29736                         .text(_t('self_intersection.error.' + errorID))();
29737                 } else {
29738                     if (nope) {   // about to un-nope, remove hint
29739                         context.ui().flash
29740                             .duration(1)
29741                             .text('')();
29742                     }
29743                 }
29744
29745
29746                 var nopeDisabled = context.surface().classed('nope-disabled');
29747                 if (nopeDisabled) {
29748                     context.surface()
29749                         .classed('nope', false)
29750                         .classed('nope-suppressed', isInvalid);
29751                 } else {
29752                     context.surface()
29753                         .classed('nope', isInvalid)
29754                         .classed('nope-suppressed', false);
29755                 }
29756
29757                 _lastLoc = loc;
29758             }
29759
29760
29761             // Uses `actionConnect.disabled()` to know whether this connection is ok..
29762             function hasRelationConflict(entity, target, edge, graph) {
29763                 var testGraph = graph.update();  // copy
29764
29765                 // if snapping to way - add midpoint there and consider that the target..
29766                 if (edge) {
29767                     var midpoint = osmNode();
29768                     var action = actionAddMidpoint({
29769                         loc: edge.loc,
29770                         edge: [target.nodes[edge.index - 1], target.nodes[edge.index]]
29771                     }, midpoint);
29772
29773                     testGraph = action(testGraph);
29774                     target = midpoint;
29775                 }
29776
29777                 // can we connect to it?
29778                 var ids = [entity.id, target.id];
29779                 return actionConnect(ids).disabled(testGraph);
29780             }
29781
29782
29783             function hasInvalidGeometry(entity, graph) {
29784                 var parents = graph.parentWays(entity);
29785                 var i, j, k;
29786
29787                 for (i = 0; i < parents.length; i++) {
29788                     var parent = parents[i];
29789                     var nodes = [];
29790                     var activeIndex = null;    // which multipolygon ring contains node being dragged
29791
29792                     // test any parent multipolygons for valid geometry
29793                     var relations = graph.parentRelations(parent);
29794                     for (j = 0; j < relations.length; j++) {
29795                         if (!relations[j].isMultipolygon()) { continue; }
29796
29797                         var rings = osmJoinWays(relations[j].members, graph);
29798
29799                         // find active ring and test it for self intersections
29800                         for (k = 0; k < rings.length; k++) {
29801                             nodes = rings[k].nodes;
29802                             if (nodes.find(function(n) { return n.id === entity.id; })) {
29803                                 activeIndex = k;
29804                                 if (geoHasSelfIntersections(nodes, entity.id)) {
29805                                     return 'multipolygonMember';
29806                                 }
29807                             }
29808                             rings[k].coords = nodes.map(function(n) { return n.loc; });
29809                         }
29810
29811                         // test active ring for intersections with other rings in the multipolygon
29812                         for (k = 0; k < rings.length; k++) {
29813                             if (k === activeIndex) { continue; }
29814
29815                             // make sure active ring doesn't cross passive rings
29816                             if (geoHasLineIntersections(rings[activeIndex].nodes, rings[k].nodes, entity.id)) {
29817                                 return 'multipolygonRing';
29818                             }
29819                         }
29820                     }
29821
29822
29823                     // If we still haven't tested this node's parent way for self-intersections.
29824                     // (because it's not a member of a multipolygon), test it now.
29825                     if (activeIndex === null) {
29826                         nodes = parent.nodes.map(function(nodeID) { return graph.entity(nodeID); });
29827                         if (nodes.length && geoHasSelfIntersections(nodes, entity.id)) {
29828                             return parent.geometry(graph);
29829                         }
29830                     }
29831
29832                 }
29833
29834                 return false;
29835             }
29836
29837
29838             function move(entity) {
29839                 if (_isCancelled) { return; }
29840                 event.sourceEvent.stopPropagation();
29841
29842                 context.surface().classed('nope-disabled', event.sourceEvent.altKey);
29843
29844                 _lastLoc = context.projection.invert(event.point);
29845
29846                 doMove(entity);
29847                 var nudge = geoViewportEdge(event.point, context.map().dimensions());
29848                 if (nudge) {
29849                     startNudge(entity, nudge);
29850                 } else {
29851                     stopNudge();
29852                 }
29853             }
29854
29855             function end(entity) {
29856                 if (_isCancelled) { return; }
29857
29858                 var wasPoint = entity.geometry(context.graph()) === 'point';
29859
29860                 var d = datum();
29861                 var nope = (d && d.properties && d.properties.nope) || context.surface().classed('nope');
29862                 var target = d && d.properties && d.properties.entity;   // entity to snap to
29863
29864                 if (nope) {   // bounce back
29865                     context.perform(
29866                         _actionBounceBack(entity.id, _startLoc)
29867                     );
29868
29869                 } else if (target && target.type === 'way') {
29870                     var choice = geoChooseEdge(context.graph().childNodes(target), context.map().mouse(), context.projection, entity.id);
29871                     context.replace(
29872                         actionAddMidpoint({
29873                             loc: choice.loc,
29874                             edge: [target.nodes[choice.index - 1], target.nodes[choice.index]]
29875                         }, entity),
29876                         connectAnnotation(entity, target)
29877                     );
29878
29879                 } else if (target && target.type === 'node' && shouldSnapToNode(target)) {
29880                     context.replace(
29881                         actionConnect([target.id, entity.id]),
29882                         connectAnnotation(entity, target)
29883                     );
29884
29885                 } else if (_wasMidpoint) {
29886                     context.replace(
29887                         actionNoop(),
29888                         _t('operations.add.annotation.vertex')
29889                     );
29890
29891                 } else {
29892                     context.replace(
29893                         actionNoop(),
29894                         moveAnnotation(entity)
29895                     );
29896                 }
29897
29898                 if (wasPoint) {
29899                     context.enter(modeSelect(context, [entity.id]));
29900
29901                 } else {
29902                     var reselection = _restoreSelectedIDs.filter(function(id) {
29903                         return context.graph().hasEntity(id);
29904                     });
29905
29906                     if (reselection.length) {
29907                         context.enter(modeSelect(context, reselection));
29908                     } else {
29909                         context.enter(modeBrowse(context));
29910                     }
29911                 }
29912             }
29913
29914
29915             function _actionBounceBack(nodeID, toLoc) {
29916                 var moveNode = actionMoveNode(nodeID, toLoc);
29917                 var action = function(graph, t) {
29918                     // last time through, pop off the bounceback perform.
29919                     // it will then overwrite the initial perform with a moveNode that does nothing
29920                     if (t === 1) { context.pop(); }
29921                     return moveNode(graph, t);
29922                 };
29923                 action.transitionable = true;
29924                 return action;
29925             }
29926
29927
29928             function cancel() {
29929                 drag.cancel();
29930                 context.enter(modeBrowse(context));
29931             }
29932
29933
29934             var drag = behaviorDrag()
29935                 .selector('.layer-touch.points .target')
29936                 .surface(context.container().select('.main-map').node())
29937                 .origin(origin)
29938                 .on('start', start)
29939                 .on('move', move)
29940                 .on('end', end);
29941
29942
29943             mode.enter = function() {
29944                 context.install(hover);
29945                 context.install(edit);
29946
29947                 select(window)
29948                     .on('keydown.dragNode', keydown)
29949                     .on('keyup.dragNode', keyup);
29950
29951                 context.history()
29952                     .on('undone.drag-node', cancel);
29953             };
29954
29955
29956             mode.exit = function() {
29957                 context.ui().sidebar.hover.cancel();
29958                 context.uninstall(hover);
29959                 context.uninstall(edit);
29960
29961                 select(window)
29962                     .on('keydown.dragNode', null)
29963                     .on('keyup.dragNode', null);
29964
29965                 context.history()
29966                     .on('undone.drag-node', null);
29967
29968                 _activeEntity = null;
29969
29970                 context.surface()
29971                     .classed('nope', false)
29972                     .classed('nope-suppressed', false)
29973                     .classed('nope-disabled', false)
29974                     .selectAll('.active')
29975                     .classed('active', false);
29976
29977                 stopNudge();
29978             };
29979
29980
29981             mode.selectedIDs = function() {
29982                 if (!arguments.length) { return _activeEntity ? [_activeEntity.id] : []; }
29983                 // no assign
29984                 return mode;
29985             };
29986
29987
29988             mode.activeID = function() {
29989                 if (!arguments.length) { return _activeEntity && _activeEntity.id; }
29990                 // no assign
29991                 return mode;
29992             };
29993
29994
29995             mode.restoreSelectedIDs = function(_) {
29996                 if (!arguments.length) { return _restoreSelectedIDs; }
29997                 _restoreSelectedIDs = _;
29998                 return mode;
29999             };
30000
30001
30002             mode.behavior = drag;
30003
30004
30005             return mode;
30006         }
30007
30008         function quickselect(arr, k, left, right, compare) {
30009             quickselectStep(arr, k, left || 0, right || (arr.length - 1), compare || defaultCompare);
30010         }
30011
30012         function quickselectStep(arr, k, left, right, compare) {
30013
30014             while (right > left) {
30015                 if (right - left > 600) {
30016                     var n = right - left + 1;
30017                     var m = k - left + 1;
30018                     var z = Math.log(n);
30019                     var s = 0.5 * Math.exp(2 * z / 3);
30020                     var sd = 0.5 * Math.sqrt(z * s * (n - s) / n) * (m - n / 2 < 0 ? -1 : 1);
30021                     var newLeft = Math.max(left, Math.floor(k - m * s / n + sd));
30022                     var newRight = Math.min(right, Math.floor(k + (n - m) * s / n + sd));
30023                     quickselectStep(arr, k, newLeft, newRight, compare);
30024                 }
30025
30026                 var t = arr[k];
30027                 var i = left;
30028                 var j = right;
30029
30030                 swap(arr, left, k);
30031                 if (compare(arr[right], t) > 0) { swap(arr, left, right); }
30032
30033                 while (i < j) {
30034                     swap(arr, i, j);
30035                     i++;
30036                     j--;
30037                     while (compare(arr[i], t) < 0) { i++; }
30038                     while (compare(arr[j], t) > 0) { j--; }
30039                 }
30040
30041                 if (compare(arr[left], t) === 0) { swap(arr, left, j); }
30042                 else {
30043                     j++;
30044                     swap(arr, j, right);
30045                 }
30046
30047                 if (j <= k) { left = j + 1; }
30048                 if (k <= j) { right = j - 1; }
30049             }
30050         }
30051
30052         function swap(arr, i, j) {
30053             var tmp = arr[i];
30054             arr[i] = arr[j];
30055             arr[j] = tmp;
30056         }
30057
30058         function defaultCompare(a, b) {
30059             return a < b ? -1 : a > b ? 1 : 0;
30060         }
30061
30062         var RBush = function RBush(maxEntries) {
30063             if ( maxEntries === void 0 ) maxEntries = 9;
30064
30065             // max entries in a node is 9 by default; min node fill is 40% for best performance
30066             this._maxEntries = Math.max(4, maxEntries);
30067             this._minEntries = Math.max(2, Math.ceil(this._maxEntries * 0.4));
30068             this.clear();
30069         };
30070
30071         RBush.prototype.all = function all () {
30072             return this._all(this.data, []);
30073         };
30074
30075         RBush.prototype.search = function search (bbox) {
30076             var node = this.data;
30077             var result = [];
30078
30079             if (!intersects(bbox, node)) { return result; }
30080
30081             var toBBox = this.toBBox;
30082             var nodesToSearch = [];
30083
30084             while (node) {
30085                 for (var i = 0; i < node.children.length; i++) {
30086                     var child = node.children[i];
30087                     var childBBox = node.leaf ? toBBox(child) : child;
30088
30089                     if (intersects(bbox, childBBox)) {
30090                         if (node.leaf) { result.push(child); }
30091                         else if (contains$1(bbox, childBBox)) { this._all(child, result); }
30092                         else { nodesToSearch.push(child); }
30093                     }
30094                 }
30095                 node = nodesToSearch.pop();
30096             }
30097
30098             return result;
30099         };
30100
30101         RBush.prototype.collides = function collides (bbox) {
30102             var node = this.data;
30103
30104             if (!intersects(bbox, node)) { return false; }
30105
30106             var nodesToSearch = [];
30107             while (node) {
30108                 for (var i = 0; i < node.children.length; i++) {
30109                     var child = node.children[i];
30110                     var childBBox = node.leaf ? this.toBBox(child) : child;
30111
30112                     if (intersects(bbox, childBBox)) {
30113                         if (node.leaf || contains$1(bbox, childBBox)) { return true; }
30114                         nodesToSearch.push(child);
30115                     }
30116                 }
30117                 node = nodesToSearch.pop();
30118             }
30119
30120             return false;
30121         };
30122
30123         RBush.prototype.load = function load (data) {
30124             if (!(data && data.length)) { return this; }
30125
30126             if (data.length < this._minEntries) {
30127                 for (var i = 0; i < data.length; i++) {
30128                     this.insert(data[i]);
30129                 }
30130                 return this;
30131             }
30132
30133             // recursively build the tree with the given data from scratch using OMT algorithm
30134             var node = this._build(data.slice(), 0, data.length - 1, 0);
30135
30136             if (!this.data.children.length) {
30137                 // save as is if tree is empty
30138                 this.data = node;
30139
30140             } else if (this.data.height === node.height) {
30141                 // split root if trees have the same height
30142                 this._splitRoot(this.data, node);
30143
30144             } else {
30145                 if (this.data.height < node.height) {
30146                     // swap trees if inserted one is bigger
30147                     var tmpNode = this.data;
30148                     this.data = node;
30149                     node = tmpNode;
30150                 }
30151
30152                 // insert the small tree into the large tree at appropriate level
30153                 this._insert(node, this.data.height - node.height - 1, true);
30154             }
30155
30156             return this;
30157         };
30158
30159         RBush.prototype.insert = function insert (item) {
30160             if (item) { this._insert(item, this.data.height - 1); }
30161             return this;
30162         };
30163
30164         RBush.prototype.clear = function clear () {
30165             this.data = createNode([]);
30166             return this;
30167         };
30168
30169         RBush.prototype.remove = function remove (item, equalsFn) {
30170             if (!item) { return this; }
30171
30172             var node = this.data;
30173             var bbox = this.toBBox(item);
30174             var path = [];
30175             var indexes = [];
30176             var i, parent, goingUp;
30177
30178             // depth-first iterative tree traversal
30179             while (node || path.length) {
30180
30181                 if (!node) { // go up
30182                     node = path.pop();
30183                     parent = path[path.length - 1];
30184                     i = indexes.pop();
30185                     goingUp = true;
30186                 }
30187
30188                 if (node.leaf) { // check current node
30189                     var index = findItem(item, node.children, equalsFn);
30190
30191                     if (index !== -1) {
30192                         // item found, remove the item and condense tree upwards
30193                         node.children.splice(index, 1);
30194                         path.push(node);
30195                         this._condense(path);
30196                         return this;
30197                     }
30198                 }
30199
30200                 if (!goingUp && !node.leaf && contains$1(node, bbox)) { // go down
30201                     path.push(node);
30202                     indexes.push(i);
30203                     i = 0;
30204                     parent = node;
30205                     node = node.children[0];
30206
30207                 } else if (parent) { // go right
30208                     i++;
30209                     node = parent.children[i];
30210                     goingUp = false;
30211
30212                 } else { node = null; } // nothing found
30213             }
30214
30215             return this;
30216         };
30217
30218         RBush.prototype.toBBox = function toBBox (item) { return item; };
30219
30220         RBush.prototype.compareMinX = function compareMinX (a, b) { return a.minX - b.minX; };
30221         RBush.prototype.compareMinY = function compareMinY (a, b) { return a.minY - b.minY; };
30222
30223         RBush.prototype.toJSON = function toJSON () { return this.data; };
30224
30225         RBush.prototype.fromJSON = function fromJSON (data) {
30226             this.data = data;
30227             return this;
30228         };
30229
30230         RBush.prototype._all = function _all (node, result) {
30231             var nodesToSearch = [];
30232             while (node) {
30233                 if (node.leaf) { result.push.apply(result, node.children); }
30234                 else { nodesToSearch.push.apply(nodesToSearch, node.children); }
30235
30236                 node = nodesToSearch.pop();
30237             }
30238             return result;
30239         };
30240
30241         RBush.prototype._build = function _build (items, left, right, height) {
30242
30243             var N = right - left + 1;
30244             var M = this._maxEntries;
30245             var node;
30246
30247             if (N <= M) {
30248                 // reached leaf level; return leaf
30249                 node = createNode(items.slice(left, right + 1));
30250                 calcBBox(node, this.toBBox);
30251                 return node;
30252             }
30253
30254             if (!height) {
30255                 // target height of the bulk-loaded tree
30256                 height = Math.ceil(Math.log(N) / Math.log(M));
30257
30258                 // target number of root entries to maximize storage utilization
30259                 M = Math.ceil(N / Math.pow(M, height - 1));
30260             }
30261
30262             node = createNode([]);
30263             node.leaf = false;
30264             node.height = height;
30265
30266             // split the items into M mostly square tiles
30267
30268             var N2 = Math.ceil(N / M);
30269             var N1 = N2 * Math.ceil(Math.sqrt(M));
30270
30271             multiSelect(items, left, right, N1, this.compareMinX);
30272
30273             for (var i = left; i <= right; i += N1) {
30274
30275                 var right2 = Math.min(i + N1 - 1, right);
30276
30277                 multiSelect(items, i, right2, N2, this.compareMinY);
30278
30279                 for (var j = i; j <= right2; j += N2) {
30280
30281                     var right3 = Math.min(j + N2 - 1, right2);
30282
30283                     // pack each entry recursively
30284                     node.children.push(this._build(items, j, right3, height - 1));
30285                 }
30286             }
30287
30288             calcBBox(node, this.toBBox);
30289
30290             return node;
30291         };
30292
30293         RBush.prototype._chooseSubtree = function _chooseSubtree (bbox, node, level, path) {
30294             while (true) {
30295                 path.push(node);
30296
30297                 if (node.leaf || path.length - 1 === level) { break; }
30298
30299                 var minArea = Infinity;
30300                 var minEnlargement = Infinity;
30301                 var targetNode = (void 0);
30302
30303                 for (var i = 0; i < node.children.length; i++) {
30304                     var child = node.children[i];
30305                     var area = bboxArea(child);
30306                     var enlargement = enlargedArea(bbox, child) - area;
30307
30308                     // choose entry with the least area enlargement
30309                     if (enlargement < minEnlargement) {
30310                         minEnlargement = enlargement;
30311                         minArea = area < minArea ? area : minArea;
30312                         targetNode = child;
30313
30314                     } else if (enlargement === minEnlargement) {
30315                         // otherwise choose one with the smallest area
30316                         if (area < minArea) {
30317                             minArea = area;
30318                             targetNode = child;
30319                         }
30320                     }
30321                 }
30322
30323                 node = targetNode || node.children[0];
30324             }
30325
30326             return node;
30327         };
30328
30329         RBush.prototype._insert = function _insert (item, level, isNode) {
30330             var bbox = isNode ? item : this.toBBox(item);
30331             var insertPath = [];
30332
30333             // find the best node for accommodating the item, saving all nodes along the path too
30334             var node = this._chooseSubtree(bbox, this.data, level, insertPath);
30335
30336             // put the item into the node
30337             node.children.push(item);
30338             extend$1(node, bbox);
30339
30340             // split on node overflow; propagate upwards if necessary
30341             while (level >= 0) {
30342                 if (insertPath[level].children.length > this._maxEntries) {
30343                     this._split(insertPath, level);
30344                     level--;
30345                 } else { break; }
30346             }
30347
30348             // adjust bboxes along the insertion path
30349             this._adjustParentBBoxes(bbox, insertPath, level);
30350         };
30351
30352         // split overflowed node into two
30353         RBush.prototype._split = function _split (insertPath, level) {
30354             var node = insertPath[level];
30355             var M = node.children.length;
30356             var m = this._minEntries;
30357
30358             this._chooseSplitAxis(node, m, M);
30359
30360             var splitIndex = this._chooseSplitIndex(node, m, M);
30361
30362             var newNode = createNode(node.children.splice(splitIndex, node.children.length - splitIndex));
30363             newNode.height = node.height;
30364             newNode.leaf = node.leaf;
30365
30366             calcBBox(node, this.toBBox);
30367             calcBBox(newNode, this.toBBox);
30368
30369             if (level) { insertPath[level - 1].children.push(newNode); }
30370             else { this._splitRoot(node, newNode); }
30371         };
30372
30373         RBush.prototype._splitRoot = function _splitRoot (node, newNode) {
30374             // split root node
30375             this.data = createNode([node, newNode]);
30376             this.data.height = node.height + 1;
30377             this.data.leaf = false;
30378             calcBBox(this.data, this.toBBox);
30379         };
30380
30381         RBush.prototype._chooseSplitIndex = function _chooseSplitIndex (node, m, M) {
30382             var index;
30383             var minOverlap = Infinity;
30384             var minArea = Infinity;
30385
30386             for (var i = m; i <= M - m; i++) {
30387                 var bbox1 = distBBox(node, 0, i, this.toBBox);
30388                 var bbox2 = distBBox(node, i, M, this.toBBox);
30389
30390                 var overlap = intersectionArea(bbox1, bbox2);
30391                 var area = bboxArea(bbox1) + bboxArea(bbox2);
30392
30393                 // choose distribution with minimum overlap
30394                 if (overlap < minOverlap) {
30395                     minOverlap = overlap;
30396                     index = i;
30397
30398                     minArea = area < minArea ? area : minArea;
30399
30400                 } else if (overlap === minOverlap) {
30401                     // otherwise choose distribution with minimum area
30402                     if (area < minArea) {
30403                         minArea = area;
30404                         index = i;
30405                     }
30406                 }
30407             }
30408
30409             return index || M - m;
30410         };
30411
30412         // sorts node children by the best axis for split
30413         RBush.prototype._chooseSplitAxis = function _chooseSplitAxis (node, m, M) {
30414             var compareMinX = node.leaf ? this.compareMinX : compareNodeMinX;
30415             var compareMinY = node.leaf ? this.compareMinY : compareNodeMinY;
30416             var xMargin = this._allDistMargin(node, m, M, compareMinX);
30417             var yMargin = this._allDistMargin(node, m, M, compareMinY);
30418
30419             // if total distributions margin value is minimal for x, sort by minX,
30420             // otherwise it's already sorted by minY
30421             if (xMargin < yMargin) { node.children.sort(compareMinX); }
30422         };
30423
30424         // total margin of all possible split distributions where each node is at least m full
30425         RBush.prototype._allDistMargin = function _allDistMargin (node, m, M, compare) {
30426             node.children.sort(compare);
30427
30428             var toBBox = this.toBBox;
30429             var leftBBox = distBBox(node, 0, m, toBBox);
30430             var rightBBox = distBBox(node, M - m, M, toBBox);
30431             var margin = bboxMargin(leftBBox) + bboxMargin(rightBBox);
30432
30433             for (var i = m; i < M - m; i++) {
30434                 var child = node.children[i];
30435                 extend$1(leftBBox, node.leaf ? toBBox(child) : child);
30436                 margin += bboxMargin(leftBBox);
30437             }
30438
30439             for (var i$1 = M - m - 1; i$1 >= m; i$1--) {
30440                 var child$1 = node.children[i$1];
30441                 extend$1(rightBBox, node.leaf ? toBBox(child$1) : child$1);
30442                 margin += bboxMargin(rightBBox);
30443             }
30444
30445             return margin;
30446         };
30447
30448         RBush.prototype._adjustParentBBoxes = function _adjustParentBBoxes (bbox, path, level) {
30449             // adjust bboxes along the given tree path
30450             for (var i = level; i >= 0; i--) {
30451                 extend$1(path[i], bbox);
30452             }
30453         };
30454
30455         RBush.prototype._condense = function _condense (path) {
30456             // go through the path, removing empty nodes and updating bboxes
30457             for (var i = path.length - 1, siblings = (void 0); i >= 0; i--) {
30458                 if (path[i].children.length === 0) {
30459                     if (i > 0) {
30460                         siblings = path[i - 1].children;
30461                         siblings.splice(siblings.indexOf(path[i]), 1);
30462
30463                     } else { this.clear(); }
30464
30465                 } else { calcBBox(path[i], this.toBBox); }
30466             }
30467         };
30468
30469         function findItem(item, items, equalsFn) {
30470             if (!equalsFn) { return items.indexOf(item); }
30471
30472             for (var i = 0; i < items.length; i++) {
30473                 if (equalsFn(item, items[i])) { return i; }
30474             }
30475             return -1;
30476         }
30477
30478         // calculate node's bbox from bboxes of its children
30479         function calcBBox(node, toBBox) {
30480             distBBox(node, 0, node.children.length, toBBox, node);
30481         }
30482
30483         // min bounding rectangle of node children from k to p-1
30484         function distBBox(node, k, p, toBBox, destNode) {
30485             if (!destNode) { destNode = createNode(null); }
30486             destNode.minX = Infinity;
30487             destNode.minY = Infinity;
30488             destNode.maxX = -Infinity;
30489             destNode.maxY = -Infinity;
30490
30491             for (var i = k; i < p; i++) {
30492                 var child = node.children[i];
30493                 extend$1(destNode, node.leaf ? toBBox(child) : child);
30494             }
30495
30496             return destNode;
30497         }
30498
30499         function extend$1(a, b) {
30500             a.minX = Math.min(a.minX, b.minX);
30501             a.minY = Math.min(a.minY, b.minY);
30502             a.maxX = Math.max(a.maxX, b.maxX);
30503             a.maxY = Math.max(a.maxY, b.maxY);
30504             return a;
30505         }
30506
30507         function compareNodeMinX(a, b) { return a.minX - b.minX; }
30508         function compareNodeMinY(a, b) { return a.minY - b.minY; }
30509
30510         function bboxArea(a)   { return (a.maxX - a.minX) * (a.maxY - a.minY); }
30511         function bboxMargin(a) { return (a.maxX - a.minX) + (a.maxY - a.minY); }
30512
30513         function enlargedArea(a, b) {
30514             return (Math.max(b.maxX, a.maxX) - Math.min(b.minX, a.minX)) *
30515                    (Math.max(b.maxY, a.maxY) - Math.min(b.minY, a.minY));
30516         }
30517
30518         function intersectionArea(a, b) {
30519             var minX = Math.max(a.minX, b.minX);
30520             var minY = Math.max(a.minY, b.minY);
30521             var maxX = Math.min(a.maxX, b.maxX);
30522             var maxY = Math.min(a.maxY, b.maxY);
30523
30524             return Math.max(0, maxX - minX) *
30525                    Math.max(0, maxY - minY);
30526         }
30527
30528         function contains$1(a, b) {
30529             return a.minX <= b.minX &&
30530                    a.minY <= b.minY &&
30531                    b.maxX <= a.maxX &&
30532                    b.maxY <= a.maxY;
30533         }
30534
30535         function intersects(a, b) {
30536             return b.minX <= a.maxX &&
30537                    b.minY <= a.maxY &&
30538                    b.maxX >= a.minX &&
30539                    b.maxY >= a.minY;
30540         }
30541
30542         function createNode(children) {
30543             return {
30544                 children: children,
30545                 height: 1,
30546                 leaf: true,
30547                 minX: Infinity,
30548                 minY: Infinity,
30549                 maxX: -Infinity,
30550                 maxY: -Infinity
30551             };
30552         }
30553
30554         // sort an array so that items come in groups of n unsorted items, with groups sorted between each other;
30555         // combines selection algorithm with binary divide & conquer approach
30556
30557         function multiSelect(arr, left, right, n, compare) {
30558             var stack = [left, right];
30559
30560             while (stack.length) {
30561                 right = stack.pop();
30562                 left = stack.pop();
30563
30564                 if (right - left <= n) { continue; }
30565
30566                 var mid = left + Math.ceil((right - left) / n / 2) * n;
30567                 quickselect(arr, mid, left, right, compare);
30568
30569                 stack.push(left, mid, mid, right);
30570             }
30571         }
30572
30573         var tiler = utilTiler();
30574         var dispatch$1 = dispatch('loaded');
30575         var _tileZoom = 14;
30576         var _krUrlRoot = 'https://www.keepright.at';
30577         var _krData = { errorTypes: {}, localizeStrings: {} };
30578
30579         // This gets reassigned if reset
30580         var _cache;
30581
30582         var _krRuleset = [
30583           // no 20 - multiple node on same spot - these are mostly boundaries overlapping roads
30584           30, 40, 50, 60, 70, 90, 100, 110, 120, 130, 150, 160, 170, 180,
30585           190, 191, 192, 193, 194, 195, 196, 197, 198,
30586           200, 201, 202, 203, 204, 205, 206, 207, 208, 210, 220,
30587           230, 231, 232, 270, 280, 281, 282, 283, 284, 285,
30588           290, 291, 292, 293, 294, 295, 296, 297, 298, 300, 310, 311, 312, 313,
30589           320, 350, 360, 370, 380, 390, 400, 401, 402, 410, 411, 412, 413
30590         ];
30591
30592
30593         function abortRequest(controller) {
30594           if (controller) {
30595             controller.abort();
30596           }
30597         }
30598
30599         function abortUnwantedRequests(cache, tiles) {
30600           Object.keys(cache.inflightTile).forEach(function (k) {
30601             var wanted = tiles.find(function (tile) { return k === tile.id; });
30602             if (!wanted) {
30603               abortRequest(cache.inflightTile[k]);
30604               delete cache.inflightTile[k];
30605             }
30606           });
30607         }
30608
30609
30610         function encodeIssueRtree(d) {
30611           return { minX: d.loc[0], minY: d.loc[1], maxX: d.loc[0], maxY: d.loc[1], data: d };
30612         }
30613
30614
30615         // Replace or remove QAItem from rtree
30616         function updateRtree(item, replace) {
30617           _cache.rtree.remove(item, function (a, b) { return a.data.id === b.data.id; });
30618
30619           if (replace) {
30620             _cache.rtree.insert(item);
30621           }
30622         }
30623
30624
30625         function tokenReplacements(d) {
30626           if (!(d instanceof QAItem)) { return; }
30627
30628           var htmlRegex = new RegExp(/<\/[a-z][\s\S]*>/);
30629           var replacements = {};
30630
30631           var issueTemplate = _krData.errorTypes[d.whichType];
30632           if (!issueTemplate) {
30633             /* eslint-disable no-console */
30634             console.log('No Template: ', d.whichType);
30635             console.log('  ', d.description);
30636             /* eslint-enable no-console */
30637             return;
30638           }
30639
30640           // some descriptions are just fixed text
30641           if (!issueTemplate.regex) { return; }
30642
30643           // regex pattern should match description with variable details captured
30644           var errorRegex = new RegExp(issueTemplate.regex, 'i');
30645           var errorMatch = errorRegex.exec(d.description);
30646           if (!errorMatch) {
30647             /* eslint-disable no-console */
30648             console.log('Unmatched: ', d.whichType);
30649             console.log('  ', d.description);
30650             console.log('  ', errorRegex);
30651             /* eslint-enable no-console */
30652             return;
30653           }
30654
30655           for (var i = 1; i < errorMatch.length; i++) {   // skip first
30656             var capture = errorMatch[i];
30657             var idType = (void 0);
30658
30659             idType = 'IDs' in issueTemplate ? issueTemplate.IDs[i-1] : '';
30660             if (idType && capture) {   // link IDs if present in the capture
30661               capture = parseError(capture, idType);
30662             } else if (htmlRegex.test(capture)) {   // escape any html in non-IDs
30663               capture = '\\' +  capture + '\\';
30664             } else {
30665               var compare = capture.toLowerCase();
30666               if (_krData.localizeStrings[compare]) {   // some replacement strings can be localized
30667                 capture = _t('QA.keepRight.error_parts.' + _krData.localizeStrings[compare]);
30668               }
30669             }
30670
30671             replacements['var' + i] = capture;
30672           }
30673
30674           return replacements;
30675         }
30676
30677
30678         function parseError(capture, idType) {
30679           var compare = capture.toLowerCase();
30680           if (_krData.localizeStrings[compare]) {   // some replacement strings can be localized
30681             capture = _t('QA.keepRight.error_parts.' + _krData.localizeStrings[compare]);
30682           }
30683
30684           switch (idType) {
30685             // link a string like "this node"
30686             case 'this':
30687               capture = linkErrorObject(capture);
30688               break;
30689
30690             case 'url':
30691               capture = linkURL(capture);
30692               break;
30693
30694             // link an entity ID
30695             case 'n':
30696             case 'w':
30697             case 'r':
30698               capture = linkEntity(idType + capture);
30699               break;
30700
30701             // some errors have more complex ID lists/variance
30702             case '20':
30703               capture = parse20(capture);
30704               break;
30705             case '211':
30706               capture = parse211(capture);
30707               break;
30708             case '231':
30709               capture = parse231(capture);
30710               break;
30711             case '294':
30712               capture = parse294(capture);
30713               break;
30714             case '370':
30715               capture = parse370(capture);
30716               break;
30717           }
30718
30719           return capture;
30720
30721
30722           function linkErrorObject(d) {
30723             return ("<a class=\"error_object_link\">" + d + "</a>");
30724           }
30725
30726           function linkEntity(d) {
30727             return ("<a class=\"error_entity_link\">" + d + "</a>");
30728           }
30729
30730           function linkURL(d) {
30731             return ("<a class=\"kr_external_link\" target=\"_blank\" href=\"" + d + "\">" + d + "</a>");
30732           }
30733
30734           // arbitrary node list of form: #ID, #ID, #ID...
30735           function parse211(capture) {
30736             var newList = [];
30737             var items = capture.split(', ');
30738
30739             items.forEach(function (item) {
30740               // ID has # at the front
30741               var id = linkEntity('n' + item.slice(1));
30742               newList.push(id);
30743             });
30744
30745             return newList.join(', ');
30746           }
30747
30748           // arbitrary way list of form: #ID(layer),#ID(layer),#ID(layer)...
30749           function parse231(capture) {
30750             var newList = [];
30751             // unfortunately 'layer' can itself contain commas, so we split on '),'
30752             var items = capture.split('),');
30753
30754             items.forEach(function (item) {
30755               var match = item.match(/\#(\d+)\((.+)\)?/);
30756               if (match !== null && match.length > 2) {
30757                 newList.push(linkEntity('w' + match[1]) + ' ' +
30758                   _t('QA.keepRight.errorTypes.231.layer', { layer: match[2] })
30759                 );
30760               }
30761             });
30762
30763             return newList.join(', ');
30764           }
30765
30766           // arbitrary node/relation list of form: from node #ID,to relation #ID,to node #ID...
30767           function parse294(capture) {
30768             var newList = [];
30769             var items = capture.split(',');
30770
30771             items.forEach(function (item) {
30772               // item of form "from/to node/relation #ID"
30773               item = item.split(' ');
30774
30775               // to/from role is more clear in quotes
30776               var role = "\"" + (item[0]) + "\"";
30777
30778               // first letter of node/relation provides the type
30779               var idType = item[1].slice(0,1);
30780
30781               // ID has # at the front
30782               var id = item[2].slice(1);
30783               id = linkEntity(idType + id);
30784
30785               newList.push((role + " " + (item[1]) + " " + id));
30786             });
30787
30788             return newList.join(', ');
30789           }
30790
30791           // may or may not include the string "(including the name 'name')"
30792           function parse370(capture) {
30793             if (!capture) { return ''; }
30794
30795             var match = capture.match(/\(including the name (\'.+\')\)/);
30796             if (match && match.length) {
30797               return _t('QA.keepRight.errorTypes.370.including_the_name', { name: match[1] });
30798             }
30799             return '';
30800           }
30801
30802           // arbitrary node list of form: #ID,#ID,#ID...
30803           function parse20(capture) {
30804             var newList = [];
30805             var items = capture.split(',');
30806
30807             items.forEach(function (item) {
30808               // ID has # at the front
30809               var id = linkEntity('n' + item.slice(1));
30810               newList.push(id);
30811             });
30812
30813             return newList.join(', ');
30814           }
30815         }
30816
30817
30818         var serviceKeepRight = {
30819           title: 'keepRight',
30820
30821           init: function init() {
30822             _mainFileFetcher.get('keepRight')
30823               .then(function (d) { return _krData = d; });
30824
30825             if (!_cache) {
30826               this.reset();
30827             }
30828
30829             this.event = utilRebind(this, dispatch$1, 'on');
30830           },
30831
30832           reset: function reset() {
30833             if (_cache) {
30834               Object.values(_cache.inflightTile).forEach(abortRequest);
30835             }
30836
30837             _cache = {
30838               data: {},
30839               loadedTile: {},
30840               inflightTile: {},
30841               inflightPost: {},
30842               closed: {},
30843               rtree: new RBush()
30844             };
30845           },
30846
30847
30848           // KeepRight API:  http://osm.mueschelsoft.de/keepright/interfacing.php
30849           loadIssues: function loadIssues(projection) {
30850             var this$1 = this;
30851
30852             var options = {
30853               format: 'geojson',
30854               ch: _krRuleset
30855             };
30856
30857             // determine the needed tiles to cover the view
30858             var tiles = tiler
30859               .zoomExtent([_tileZoom, _tileZoom])
30860               .getTiles(projection);
30861
30862             // abort inflight requests that are no longer needed
30863             abortUnwantedRequests(_cache, tiles);
30864
30865             // issue new requests..
30866             tiles.forEach(function (tile) {
30867               if (_cache.loadedTile[tile.id] || _cache.inflightTile[tile.id]) { return; }
30868
30869               var ref = tile.extent.rectangle();
30870               var left = ref[0];
30871               var top = ref[1];
30872               var right = ref[2];
30873               var bottom = ref[3];
30874               var params = Object.assign({}, options, { left: left, bottom: bottom, right: right, top: top });
30875               var url = _krUrlRoot + "/export.php?" + utilQsString(params);
30876               var controller = new AbortController();
30877
30878               _cache.inflightTile[tile.id] = controller;
30879
30880               d3_json(url, { signal: controller.signal })
30881                 .then(function (data) {
30882                   delete _cache.inflightTile[tile.id];
30883                   _cache.loadedTile[tile.id] = true;
30884                   if (!data || !data.features || !data.features.length) {
30885                     throw new Error('No Data');
30886                   }
30887
30888                   data.features.forEach(function (feature) {
30889                     var feature_properties = feature.properties;
30890                     var itemType = feature_properties.error_type;
30891                     var id = feature_properties.error_id;
30892                     var comment = feature_properties.comment; if ( comment === void 0 ) comment = null;
30893                     var objectId = feature_properties.object_id;
30894                     var objectType = feature_properties.object_type;
30895                     var schema = feature_properties.schema;
30896                     var title = feature_properties.title;
30897                     var loc = feature.geometry.coordinates;
30898                     var description = feature.properties.description; if ( description === void 0 ) description = '';
30899
30900                     // if there is a parent, save its error type e.g.:
30901                     //  Error 191 = "highway-highway"
30902                     //  Error 190 = "intersections without junctions"  (parent)
30903                     var issueTemplate = _krData.errorTypes[itemType];
30904                     var parentIssueType = (Math.floor(itemType / 10) * 10).toString();
30905
30906                     // try to handle error type directly, fallback to parent error type.
30907                     var whichType = issueTemplate ? itemType : parentIssueType;
30908                     var whichTemplate = _krData.errorTypes[whichType];
30909
30910                     // Rewrite a few of the errors at this point..
30911                     // This is done to make them easier to linkify and translate.
30912                     switch (whichType) {
30913                       case '170':
30914                         description = "This feature has a FIXME tag: " + description;
30915                         break;
30916                       case '292':
30917                       case '293':
30918                         description = description.replace('A turn-', 'This turn-');
30919                         break;
30920                       case '294':
30921                       case '295':
30922                       case '296':
30923                       case '297':
30924                       case '298':
30925                         description = "This turn-restriction~" + description;
30926                         break;
30927                       case '300':
30928                         description = 'This highway is missing a maxspeed tag';
30929                         break;
30930                       case '411':
30931                       case '412':
30932                       case '413':
30933                         description = "This feature~" + description;
30934                         break;
30935                     }
30936
30937                     // move markers slightly so it doesn't obscure the geometry,
30938                     // then move markers away from other coincident markers
30939                     var coincident = false;
30940                     do {
30941                       // first time, move marker up. after that, move marker right.
30942                       var delta = coincident ? [0.00001, 0] : [0, 0.00001];
30943                       loc = geoVecAdd(loc, delta);
30944                       var bbox = geoExtent(loc).bbox();
30945                       coincident = _cache.rtree.search(bbox).length;
30946                     } while (coincident);
30947
30948                     var d = new QAItem(loc, this$1, itemType, id, {
30949                       comment: comment,
30950                       description: description,
30951                       whichType: whichType,
30952                       parentIssueType: parentIssueType,
30953                       severity: whichTemplate.severity || 'error',
30954                       objectId: objectId,
30955                       objectType: objectType,
30956                       schema: schema,
30957                       title: title
30958                     });
30959
30960                     d.replacements = tokenReplacements(d);
30961
30962                     _cache.data[id] = d;
30963                     _cache.rtree.insert(encodeIssueRtree(d));
30964                   });
30965
30966                   dispatch$1.call('loaded');
30967                 })
30968                 .catch(function () {
30969                   delete _cache.inflightTile[tile.id];
30970                   _cache.loadedTile[tile.id] = true;
30971                 });
30972
30973             });
30974           },
30975
30976
30977           postUpdate: function postUpdate(d, callback) {
30978             var this$1 = this;
30979
30980             if (_cache.inflightPost[d.id]) {
30981               return callback({ message: 'Error update already inflight', status: -2 }, d);
30982             }
30983
30984             var params = { schema: d.schema, id: d.id };
30985
30986             if (d.newStatus) {
30987               params.st = d.newStatus;
30988             }
30989             if (d.newComment !== undefined) {
30990               params.co = d.newComment;
30991             }
30992
30993             // NOTE: This throws a CORS err, but it seems successful.
30994             // We don't care too much about the response, so this is fine.
30995             var url = _krUrlRoot + "/comment.php?" + utilQsString(params);
30996             var controller = new AbortController();
30997
30998             _cache.inflightPost[d.id] = controller;
30999
31000             // Since this is expected to throw an error just continue as if it worked
31001             // (worst case scenario the request truly fails and issue will show up if iD restarts)
31002             d3_json(url, { signal: controller.signal })
31003               .finally(function () {
31004                 delete _cache.inflightPost[d.id];
31005
31006                 if (d.newStatus === 'ignore') {
31007                   // ignore permanently (false positive)
31008                   this$1.removeItem(d);
31009                 } else if (d.newStatus === 'ignore_t') {
31010                   // ignore temporarily (error fixed)
31011                   this$1.removeItem(d);
31012                   _cache.closed[((d.schema) + ":" + (d.id))] = true;
31013                 } else {
31014                   d = this$1.replaceItem(d.update({
31015                     comment: d.newComment,
31016                     newComment: undefined,
31017                     newState: undefined
31018                   }));
31019                 }
31020
31021                 if (callback) { callback(null, d); }
31022               });
31023           },
31024
31025           // Get all cached QAItems covering the viewport
31026           getItems: function getItems(projection) {
31027             var viewport = projection.clipExtent();
31028             var min = [viewport[0][0], viewport[1][1]];
31029             var max = [viewport[1][0], viewport[0][1]];
31030             var bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox();
31031
31032             return _cache.rtree.search(bbox).map(function (d) { return d.data; });
31033           },
31034
31035           // Get a QAItem from cache
31036           // NOTE: Don't change method name until UI v3 is merged
31037           getError: function getError(id) {
31038             return _cache.data[id];
31039           },
31040
31041           // Replace a single QAItem in the cache
31042           replaceItem: function replaceItem(item) {
31043             if (!(item instanceof QAItem) || !item.id) { return; }
31044
31045             _cache.data[item.id] = item;
31046             updateRtree(encodeIssueRtree(item), true); // true = replace
31047             return item;
31048           },
31049
31050           // Remove a single QAItem from the cache
31051           removeItem: function removeItem(item) {
31052             if (!(item instanceof QAItem) || !item.id) { return; }
31053
31054             delete _cache.data[item.id];
31055             updateRtree(encodeIssueRtree(item), false); // false = remove
31056           },
31057
31058           issueURL: function issueURL(item) {
31059             return (_krUrlRoot + "/report_map.php?schema=" + (item.schema) + "&error=" + (item.id));
31060           },
31061
31062           // Get an array of issues closed during this session.
31063           // Used to populate `closed:keepright` changeset tag
31064           getClosedIDs: function getClosedIDs() {
31065             return Object.keys(_cache.closed).sort();
31066           }
31067
31068         };
31069
31070         var tiler$1 = utilTiler();
31071         var dispatch$2 = dispatch('loaded');
31072         var _tileZoom$1 = 14;
31073         var _impOsmUrls = {
31074           ow: 'https://grab.community.improve-osm.org/directionOfFlowService',
31075           mr: 'https://grab.community.improve-osm.org/missingGeoService',
31076           tr: 'https://grab.community.improve-osm.org/turnRestrictionService'
31077         };
31078         var _impOsmData = { icons: {} };
31079
31080
31081         // This gets reassigned if reset
31082         var _cache$1;
31083
31084         function abortRequest$1(i) {
31085           Object.values(i).forEach(function (controller) {
31086             if (controller) {
31087               controller.abort();
31088             }
31089           });
31090         }
31091
31092         function abortUnwantedRequests$1(cache, tiles) {
31093           Object.keys(cache.inflightTile).forEach(function (k) {
31094             var wanted = tiles.find(function (tile) { return k === tile.id; });
31095             if (!wanted) {
31096               abortRequest$1(cache.inflightTile[k]);
31097               delete cache.inflightTile[k];
31098             }
31099           });
31100         }
31101
31102         function encodeIssueRtree$1(d) {
31103           return { minX: d.loc[0], minY: d.loc[1], maxX: d.loc[0], maxY: d.loc[1], data: d };
31104         }
31105
31106         // Replace or remove QAItem from rtree
31107         function updateRtree$1(item, replace) {
31108           _cache$1.rtree.remove(item, function (a, b) { return a.data.id === b.data.id; });
31109
31110           if (replace) {
31111             _cache$1.rtree.insert(item);
31112           }
31113         }
31114
31115         function linkErrorObject(d) {
31116           return ("<a class=\"error_object_link\">" + d + "</a>");
31117         }
31118
31119         function linkEntity(d) {
31120           return ("<a class=\"error_entity_link\">" + d + "</a>");
31121         }
31122
31123         function pointAverage(points) {
31124           if (points.length) {
31125             var sum = points.reduce(
31126               function (acc, point) { return geoVecAdd(acc, [point.lon, point.lat]); },
31127               [0,0]
31128             );
31129             return geoVecScale(sum, 1 / points.length);
31130           } else {
31131             return [0,0];
31132           }
31133         }
31134
31135         function relativeBearing(p1, p2) {
31136           var angle = Math.atan2(p2.lon - p1.lon, p2.lat - p1.lat);
31137           if (angle < 0) {
31138             angle += 2 * Math.PI;
31139           }
31140
31141           // Return degrees
31142           return angle * 180 / Math.PI;
31143         }
31144
31145         // Assuming range [0,360)
31146         function cardinalDirection(bearing) {
31147           var dir = 45 * Math.round(bearing / 45);
31148           var compass = {
31149             0: 'north',
31150             45: 'northeast',
31151             90: 'east',
31152             135: 'southeast',
31153             180: 'south',
31154             225: 'southwest',
31155             270: 'west',
31156             315: 'northwest',
31157             360: 'north'
31158           };
31159
31160           return _t(("QA.improveOSM.directions." + (compass[dir])));
31161         }
31162
31163         // Errors shouldn't obscure each other
31164         function preventCoincident(loc, bumpUp) {
31165           var coincident = false;
31166           do {
31167             // first time, move marker up. after that, move marker right.
31168             var delta = coincident ? [0.00001, 0] : (bumpUp ? [0, 0.00001] : [0, 0]);
31169             loc = geoVecAdd(loc, delta);
31170             var bbox = geoExtent(loc).bbox();
31171             coincident = _cache$1.rtree.search(bbox).length;
31172           } while (coincident);
31173
31174           return loc;
31175         }
31176
31177         var serviceImproveOSM = {
31178           title: 'improveOSM',
31179
31180           init: function init() {
31181             _mainFileFetcher.get('qa_data')
31182               .then(function (d) { return _impOsmData = d.improveOSM; });
31183
31184             if (!_cache$1) {
31185               this.reset();
31186             }
31187
31188             this.event = utilRebind(this, dispatch$2, 'on');
31189           },
31190
31191           reset: function reset() {
31192             if (_cache$1) {
31193               Object.values(_cache$1.inflightTile).forEach(abortRequest$1);
31194             }
31195             _cache$1 = {
31196               data: {},
31197               loadedTile: {},
31198               inflightTile: {},
31199               inflightPost: {},
31200               closed: {},
31201               rtree: new RBush()
31202             };
31203           },
31204
31205           loadIssues: function loadIssues(projection) {
31206             var this$1 = this;
31207
31208             var options = {
31209               client: 'iD',
31210               status: 'OPEN',
31211               zoom: '19' // Use a high zoom so that clusters aren't returned
31212             };
31213
31214             // determine the needed tiles to cover the view
31215             var tiles = tiler$1
31216               .zoomExtent([_tileZoom$1, _tileZoom$1])
31217               .getTiles(projection);
31218
31219             // abort inflight requests that are no longer needed
31220             abortUnwantedRequests$1(_cache$1, tiles);
31221
31222             // issue new requests..
31223             tiles.forEach(function (tile) {
31224               if (_cache$1.loadedTile[tile.id] || _cache$1.inflightTile[tile.id]) { return; }
31225
31226               var ref = tile.extent.rectangle();
31227               var east = ref[0];
31228               var north = ref[1];
31229               var west = ref[2];
31230               var south = ref[3];
31231               var params = Object.assign({}, options, { east: east, south: south, west: west, north: north });
31232
31233               // 3 separate requests to store for each tile
31234               var requests = {};
31235
31236               Object.keys(_impOsmUrls).forEach(function (k) {
31237                 // We exclude WATER from missing geometry as it doesn't seem useful
31238                 // We use most confident one-way and turn restrictions only, still have false positives
31239                 var kParams = Object.assign({},
31240                   params,
31241                   (k === 'mr') ? { type: 'PARKING,ROAD,BOTH,PATH' } : { confidenceLevel: 'C1' }
31242                 );
31243                 var url = (_impOsmUrls[k]) + "/search?" + utilQsString(kParams);
31244                 var controller = new AbortController();
31245
31246                 requests[k] = controller;
31247
31248                 d3_json(url, { signal: controller.signal })
31249                   .then(function (data) {
31250                     delete _cache$1.inflightTile[tile.id][k];
31251                     if (!Object.keys(_cache$1.inflightTile[tile.id]).length) {
31252                       delete _cache$1.inflightTile[tile.id];
31253                       _cache$1.loadedTile[tile.id] = true;
31254                     }
31255
31256                     // Road segments at high zoom == oneways
31257                     if (data.roadSegments) {
31258                       data.roadSegments.forEach(function (feature) {
31259                         // Position error at the approximate middle of the segment
31260                         var points = feature.points;
31261                         var wayId = feature.wayId;
31262                         var fromNodeId = feature.fromNodeId;
31263                         var toNodeId = feature.toNodeId;
31264                         var itemId = "" + wayId + fromNodeId + toNodeId;
31265                         var mid = points.length / 2;
31266                         var loc;
31267
31268                         // Even number of points, find midpoint of the middle two
31269                         // Odd number of points, use position of very middle point
31270                         if (mid % 1 === 0) {
31271                           loc = pointAverage([points[mid - 1], points[mid]]);
31272                         } else {
31273                           mid = points[Math.floor(mid)];
31274                           loc = [mid.lon, mid.lat];
31275                         }
31276
31277                         // One-ways can land on same segment in opposite direction
31278                         loc = preventCoincident(loc, false);
31279
31280                         var d = new QAItem(loc, this$1, k, itemId, {
31281                           issueKey: k, // used as a category
31282                           identifier: { // used to post changes
31283                             wayId: wayId,
31284                             fromNodeId: fromNodeId,
31285                             toNodeId: toNodeId
31286                           },
31287                           objectId: wayId,
31288                           objectType: 'way'
31289                         });
31290
31291                         // Variables used in the description
31292                         d.replacements = {
31293                           percentage: feature.percentOfTrips,
31294                           num_trips: feature.numberOfTrips,
31295                           highway: linkErrorObject(_t('QA.keepRight.error_parts.highway')),
31296                           from_node: linkEntity('n' + feature.fromNodeId),
31297                           to_node: linkEntity('n' + feature.toNodeId)
31298                         };
31299
31300                         _cache$1.data[d.id] = d;
31301                         _cache$1.rtree.insert(encodeIssueRtree$1(d));
31302                       });
31303                     }
31304
31305                     // Tiles at high zoom == missing roads
31306                     if (data.tiles) {
31307                       data.tiles.forEach(function (feature) {
31308                         var type = feature.type;
31309                         var x = feature.x;
31310                         var y = feature.y;
31311                         var numberOfTrips = feature.numberOfTrips;
31312                         var geoType = type.toLowerCase();
31313                         var itemId = "" + geoType + x + y + numberOfTrips;
31314
31315                         // Average of recorded points should land on the missing geometry
31316                         // Missing geometry could happen to land on another error
31317                         var loc = pointAverage(feature.points);
31318                         loc = preventCoincident(loc, false);
31319
31320                         var d = new QAItem(loc, this$1, (k + "-" + geoType), itemId, {
31321                           issueKey: k,
31322                           identifier: { x: x, y: y }
31323                         });
31324
31325                         d.replacements = {
31326                           num_trips: numberOfTrips,
31327                           geometry_type: _t(("QA.improveOSM.geometry_types." + geoType))
31328                         };
31329
31330                         // -1 trips indicates data came from a 3rd party
31331                         if (numberOfTrips === -1) {
31332                           d.desc = _t('QA.improveOSM.error_types.mr.description_alt', d.replacements);
31333                         }
31334
31335                         _cache$1.data[d.id] = d;
31336                         _cache$1.rtree.insert(encodeIssueRtree$1(d));
31337                       });
31338                     }
31339
31340                     // Entities at high zoom == turn restrictions
31341                     if (data.entities) {
31342                       data.entities.forEach(function (feature) {
31343                         var point = feature.point;
31344                         var id = feature.id;
31345                         var segments = feature.segments;
31346                         var numberOfPasses = feature.numberOfPasses;
31347                         var turnType = feature.turnType;
31348                         var itemId = "" + (id.replace(/[,:+#]/g, '_'));
31349
31350                         // Turn restrictions could be missing at same junction
31351                         // We also want to bump the error up so node is accessible
31352                         var loc = preventCoincident([point.lon, point.lat], true);
31353
31354                         // Elements are presented in a strange way
31355                         var ids = id.split(',');
31356                         var from_way = ids[0];
31357                         var via_node = ids[3];
31358                         var to_way = ids[2].split(':')[1];
31359
31360                         var d = new QAItem(loc, this$1, k, itemId, {
31361                           issueKey: k,
31362                           identifier: id,
31363                           objectId: via_node,
31364                           objectType: 'node'
31365                         });
31366
31367                         // Travel direction along from_way clarifies the turn restriction
31368                         var ref = segments[0].points;
31369                         var p1 = ref[0];
31370                         var p2 = ref[1];
31371                         var dir_of_travel = cardinalDirection(relativeBearing(p1, p2));
31372
31373                         // Variables used in the description
31374                         d.replacements = {
31375                           num_passed: numberOfPasses,
31376                           num_trips: segments[0].numberOfTrips,
31377                           turn_restriction: turnType.toLowerCase(),
31378                           from_way: linkEntity('w' + from_way),
31379                           to_way: linkEntity('w' + to_way),
31380                           travel_direction: dir_of_travel,
31381                           junction: linkErrorObject(_t('QA.keepRight.error_parts.this_node'))
31382                         };
31383
31384                         _cache$1.data[d.id] = d;
31385                         _cache$1.rtree.insert(encodeIssueRtree$1(d));
31386                         dispatch$2.call('loaded');
31387                       });
31388                     }
31389                   })
31390                   .catch(function () {
31391                     delete _cache$1.inflightTile[tile.id][k];
31392                     if (!Object.keys(_cache$1.inflightTile[tile.id]).length) {
31393                       delete _cache$1.inflightTile[tile.id];
31394                       _cache$1.loadedTile[tile.id] = true;
31395                     }
31396                   });
31397               });
31398
31399               _cache$1.inflightTile[tile.id] = requests;
31400             });
31401           },
31402
31403           getComments: function getComments(item) {
31404             var this$1 = this;
31405
31406             // If comments already retrieved no need to do so again
31407             if (item.comments) {
31408               return Promise.resolve(item);
31409             }
31410
31411             var key = item.issueKey;
31412             var qParams = {};
31413
31414             if (key === 'ow') {
31415               qParams = item.identifier;
31416             } else if (key === 'mr') {
31417               qParams.tileX = item.identifier.x;
31418               qParams.tileY = item.identifier.y;
31419             } else if (key === 'tr') {
31420               qParams.targetId = item.identifier;
31421             }
31422
31423             var url = (_impOsmUrls[key]) + "/retrieveComments?" + utilQsString(qParams);
31424             var cacheComments = function (data) {
31425               // Assign directly for immediate use afterwards
31426               // comments are served newest to oldest
31427               item.comments = data.comments ? data.comments.reverse() : [];
31428               this$1.replaceItem(item);
31429             };
31430
31431             return d3_json(url).then(cacheComments).then(function () { return item; });
31432           },
31433
31434           postUpdate: function postUpdate(d, callback) {
31435             if (!serviceOsm.authenticated()) { // Username required in payload
31436               return callback({ message: 'Not Authenticated', status: -3}, d);
31437             }
31438             if (_cache$1.inflightPost[d.id]) {
31439               return callback({ message: 'Error update already inflight', status: -2 }, d);
31440             }
31441
31442             // Payload can only be sent once username is established
31443             serviceOsm.userDetails(sendPayload.bind(this));
31444
31445             function sendPayload(err, user) {
31446               var this$1 = this;
31447
31448               if (err) { return callback(err, d); }
31449
31450               var key = d.issueKey;
31451               var url = (_impOsmUrls[key]) + "/comment";
31452               var payload = {
31453                 username: user.display_name,
31454                 targetIds: [ d.identifier ]
31455               };
31456
31457               if (d.newStatus) {
31458                 payload.status = d.newStatus;
31459                 payload.text = 'status changed';
31460               }
31461
31462               // Comment take place of default text
31463               if (d.newComment) {
31464                 payload.text = d.newComment;
31465               }
31466
31467               var controller = new AbortController();
31468               _cache$1.inflightPost[d.id] = controller;
31469
31470               var options = {
31471                 method: 'POST',
31472                 signal: controller.signal,
31473                 body: JSON.stringify(payload)
31474               };
31475
31476               d3_json(url, options)
31477                 .then(function () {
31478                   delete _cache$1.inflightPost[d.id];
31479
31480                   // Just a comment, update error in cache
31481                   if (!d.newStatus) {
31482                     var now = new Date();
31483                     var comments = d.comments ? d.comments : [];
31484
31485                     comments.push({
31486                       username: payload.username,
31487                       text: payload.text,
31488                       timestamp: now.getTime() / 1000
31489                     });
31490
31491                     this$1.replaceItem(d.update({
31492                       comments: comments,
31493                       newComment: undefined
31494                     }));
31495                   } else {
31496                     this$1.removeItem(d);
31497                     if (d.newStatus === 'SOLVED') {
31498                       // Keep track of the number of issues closed per type to tag the changeset
31499                       if (!(d.issueKey in _cache$1.closed)) {
31500                         _cache$1.closed[d.issueKey] = 0;
31501                       }
31502                       _cache$1.closed[d.issueKey] += 1;
31503                     }
31504                   }
31505                   if (callback) { callback(null, d); }
31506                 })
31507                 .catch(function (err) {
31508                   delete _cache$1.inflightPost[d.id];
31509                   if (callback) { callback(err.message); }
31510                 });
31511             }
31512           },
31513
31514
31515           // Get all cached QAItems covering the viewport
31516           getItems: function getItems(projection) {
31517             var viewport = projection.clipExtent();
31518             var min = [viewport[0][0], viewport[1][1]];
31519             var max = [viewport[1][0], viewport[0][1]];
31520             var bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox();
31521
31522             return _cache$1.rtree.search(bbox).map(function (d) { return d.data; });
31523           },
31524
31525           // Get a QAItem from cache
31526           // NOTE: Don't change method name until UI v3 is merged
31527           getError: function getError(id) {
31528             return _cache$1.data[id];
31529           },
31530
31531           // get the name of the icon to display for this item
31532           getIcon: function getIcon(itemType) {
31533             return _impOsmData.icons[itemType];
31534           },
31535
31536           // Replace a single QAItem in the cache
31537           replaceItem: function replaceItem(issue) {
31538             if (!(issue instanceof QAItem) || !issue.id) { return; }
31539
31540             _cache$1.data[issue.id] = issue;
31541             updateRtree$1(encodeIssueRtree$1(issue), true); // true = replace
31542             return issue;
31543           },
31544
31545           // Remove a single QAItem from the cache
31546           removeItem: function removeItem(issue) {
31547             if (!(issue instanceof QAItem) || !issue.id) { return; }
31548
31549             delete _cache$1.data[issue.id];
31550             updateRtree$1(encodeIssueRtree$1(issue), false); // false = remove
31551           },
31552
31553           // Used to populate `closed:improveosm:*` changeset tags
31554           getClosedCounts: function getClosedCounts() {
31555             return _cache$1.closed;
31556           }
31557         };
31558
31559         var defaults = createCommonjsModule(function (module) {
31560         function getDefaults() {
31561           return {
31562             baseUrl: null,
31563             breaks: false,
31564             gfm: true,
31565             headerIds: true,
31566             headerPrefix: '',
31567             highlight: null,
31568             langPrefix: 'language-',
31569             mangle: true,
31570             pedantic: false,
31571             renderer: null,
31572             sanitize: false,
31573             sanitizer: null,
31574             silent: false,
31575             smartLists: false,
31576             smartypants: false,
31577             tokenizer: null,
31578             xhtml: false
31579           };
31580         }
31581
31582         function changeDefaults(newDefaults) {
31583           module.exports.defaults = newDefaults;
31584         }
31585
31586         module.exports = {
31587           defaults: getDefaults(),
31588           getDefaults: getDefaults,
31589           changeDefaults: changeDefaults
31590         };
31591         });
31592
31593         /**
31594          * Helpers
31595          */
31596         var escapeTest = /[&<>"']/;
31597         var escapeReplace = /[&<>"']/g;
31598         var escapeTestNoEncode = /[<>"']|&(?!#?\w+;)/;
31599         var escapeReplaceNoEncode = /[<>"']|&(?!#?\w+;)/g;
31600         var escapeReplacements = {
31601           '&': '&amp;',
31602           '<': '&lt;',
31603           '>': '&gt;',
31604           '"': '&quot;',
31605           "'": '&#39;'
31606         };
31607         var getEscapeReplacement = function (ch) { return escapeReplacements[ch]; };
31608         function escape$1(html, encode) {
31609           if (encode) {
31610             if (escapeTest.test(html)) {
31611               return html.replace(escapeReplace, getEscapeReplacement);
31612             }
31613           } else {
31614             if (escapeTestNoEncode.test(html)) {
31615               return html.replace(escapeReplaceNoEncode, getEscapeReplacement);
31616             }
31617           }
31618
31619           return html;
31620         }
31621
31622         var unescapeTest = /&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/ig;
31623
31624         function unescape$1(html) {
31625           // explicitly match decimal, hex, and named HTML entities
31626           return html.replace(unescapeTest, function (_, n) {
31627             n = n.toLowerCase();
31628             if (n === 'colon') { return ':'; }
31629             if (n.charAt(0) === '#') {
31630               return n.charAt(1) === 'x'
31631                 ? String.fromCharCode(parseInt(n.substring(2), 16))
31632                 : String.fromCharCode(+n.substring(1));
31633             }
31634             return '';
31635           });
31636         }
31637
31638         var caret = /(^|[^\[])\^/g;
31639         function edit(regex, opt) {
31640           regex = regex.source || regex;
31641           opt = opt || '';
31642           var obj = {
31643             replace: function (name, val) {
31644               val = val.source || val;
31645               val = val.replace(caret, '$1');
31646               regex = regex.replace(name, val);
31647               return obj;
31648             },
31649             getRegex: function () {
31650               return new RegExp(regex, opt);
31651             }
31652           };
31653           return obj;
31654         }
31655
31656         var nonWordAndColonTest = /[^\w:]/g;
31657         var originIndependentUrl = /^$|^[a-z][a-z0-9+.-]*:|^[?#]/i;
31658         function cleanUrl(sanitize, base, href) {
31659           if (sanitize) {
31660             var prot;
31661             try {
31662               prot = decodeURIComponent(unescape$1(href))
31663                 .replace(nonWordAndColonTest, '')
31664                 .toLowerCase();
31665             } catch (e) {
31666               return null;
31667             }
31668             if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0 || prot.indexOf('data:') === 0) {
31669               return null;
31670             }
31671           }
31672           if (base && !originIndependentUrl.test(href)) {
31673             href = resolveUrl(base, href);
31674           }
31675           try {
31676             href = encodeURI(href).replace(/%25/g, '%');
31677           } catch (e$1) {
31678             return null;
31679           }
31680           return href;
31681         }
31682
31683         var baseUrls = {};
31684         var justDomain = /^[^:]+:\/*[^/]*$/;
31685         var protocol = /^([^:]+:)[\s\S]*$/;
31686         var domain = /^([^:]+:\/*[^/]*)[\s\S]*$/;
31687
31688         function resolveUrl(base, href) {
31689           if (!baseUrls[' ' + base]) {
31690             // we can ignore everything in base after the last slash of its path component,
31691             // but we might need to add _that_
31692             // https://tools.ietf.org/html/rfc3986#section-3
31693             if (justDomain.test(base)) {
31694               baseUrls[' ' + base] = base + '/';
31695             } else {
31696               baseUrls[' ' + base] = rtrim(base, '/', true);
31697             }
31698           }
31699           base = baseUrls[' ' + base];
31700           var relativeBase = base.indexOf(':') === -1;
31701
31702           if (href.substring(0, 2) === '//') {
31703             if (relativeBase) {
31704               return href;
31705             }
31706             return base.replace(protocol, '$1') + href;
31707           } else if (href.charAt(0) === '/') {
31708             if (relativeBase) {
31709               return href;
31710             }
31711             return base.replace(domain, '$1') + href;
31712           } else {
31713             return base + href;
31714           }
31715         }
31716
31717         var noopTest = { exec: function noopTest() {} };
31718
31719         function merge$1(obj) {
31720           var arguments$1 = arguments;
31721
31722           var i = 1,
31723             target,
31724             key;
31725
31726           for (; i < arguments.length; i++) {
31727             target = arguments$1[i];
31728             for (key in target) {
31729               if (Object.prototype.hasOwnProperty.call(target, key)) {
31730                 obj[key] = target[key];
31731               }
31732             }
31733           }
31734
31735           return obj;
31736         }
31737
31738         function splitCells(tableRow, count) {
31739           // ensure that every cell-delimiting pipe has a space
31740           // before it to distinguish it from an escaped pipe
31741           var row = tableRow.replace(/\|/g, function (match, offset, str) {
31742               var escaped = false,
31743                 curr = offset;
31744               while (--curr >= 0 && str[curr] === '\\') { escaped = !escaped; }
31745               if (escaped) {
31746                 // odd number of slashes means | is escaped
31747                 // so we leave it alone
31748                 return '|';
31749               } else {
31750                 // add space before unescaped |
31751                 return ' |';
31752               }
31753             }),
31754             cells = row.split(/ \|/);
31755           var i = 0;
31756
31757           if (cells.length > count) {
31758             cells.splice(count);
31759           } else {
31760             while (cells.length < count) { cells.push(''); }
31761           }
31762
31763           for (; i < cells.length; i++) {
31764             // leading or trailing whitespace is ignored per the gfm spec
31765             cells[i] = cells[i].trim().replace(/\\\|/g, '|');
31766           }
31767           return cells;
31768         }
31769
31770         // Remove trailing 'c's. Equivalent to str.replace(/c*$/, '').
31771         // /c*$/ is vulnerable to REDOS.
31772         // invert: Remove suffix of non-c chars instead. Default falsey.
31773         function rtrim(str, c, invert) {
31774           var l = str.length;
31775           if (l === 0) {
31776             return '';
31777           }
31778
31779           // Length of suffix matching the invert condition.
31780           var suffLen = 0;
31781
31782           // Step left until we fail to match the invert condition.
31783           while (suffLen < l) {
31784             var currChar = str.charAt(l - suffLen - 1);
31785             if (currChar === c && !invert) {
31786               suffLen++;
31787             } else if (currChar !== c && invert) {
31788               suffLen++;
31789             } else {
31790               break;
31791             }
31792           }
31793
31794           return str.substr(0, l - suffLen);
31795         }
31796
31797         function findClosingBracket(str, b) {
31798           if (str.indexOf(b[1]) === -1) {
31799             return -1;
31800           }
31801           var l = str.length;
31802           var level = 0,
31803             i = 0;
31804           for (; i < l; i++) {
31805             if (str[i] === '\\') {
31806               i++;
31807             } else if (str[i] === b[0]) {
31808               level++;
31809             } else if (str[i] === b[1]) {
31810               level--;
31811               if (level < 0) {
31812                 return i;
31813               }
31814             }
31815           }
31816           return -1;
31817         }
31818
31819         function checkSanitizeDeprecation(opt) {
31820           if (opt && opt.sanitize && !opt.silent) {
31821             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');
31822           }
31823         }
31824
31825         var helpers = {
31826           escape: escape$1,
31827           unescape: unescape$1,
31828           edit: edit,
31829           cleanUrl: cleanUrl,
31830           resolveUrl: resolveUrl,
31831           noopTest: noopTest,
31832           merge: merge$1,
31833           splitCells: splitCells,
31834           rtrim: rtrim,
31835           findClosingBracket: findClosingBracket,
31836           checkSanitizeDeprecation: checkSanitizeDeprecation
31837         };
31838
31839         var defaults$1 = defaults.defaults;
31840         var rtrim$1 = helpers.rtrim;
31841         var splitCells$1 = helpers.splitCells;
31842         var escape$2 = helpers.escape;
31843         var findClosingBracket$1 = helpers.findClosingBracket;
31844
31845         function outputLink(cap, link, raw) {
31846           var href = link.href;
31847           var title = link.title ? escape$2(link.title) : null;
31848
31849           if (cap[0].charAt(0) !== '!') {
31850             return {
31851               type: 'link',
31852               raw: raw,
31853               href: href,
31854               title: title,
31855               text: cap[1]
31856             };
31857           } else {
31858             return {
31859               type: 'image',
31860               raw: raw,
31861               text: escape$2(cap[1]),
31862               href: href,
31863               title: title
31864             };
31865           }
31866         }
31867
31868         /**
31869          * Tokenizer
31870          */
31871         var Tokenizer_1 = /*@__PURE__*/(function () {
31872           function Tokenizer(options) {
31873             this.options = options || defaults$1;
31874           }
31875
31876           Tokenizer.prototype.space = function space (src) {
31877             var cap = this.rules.block.newline.exec(src);
31878             if (cap) {
31879               if (cap[0].length > 1) {
31880                 return {
31881                   type: 'space',
31882                   raw: cap[0]
31883                 };
31884               }
31885               return { raw: '\n' };
31886             }
31887           };
31888
31889           Tokenizer.prototype.code = function code (src, tokens) {
31890             var cap = this.rules.block.code.exec(src);
31891             if (cap) {
31892               var lastToken = tokens[tokens.length - 1];
31893               // An indented code block cannot interrupt a paragraph.
31894               if (lastToken && lastToken.type === 'paragraph') {
31895                 tokens.pop();
31896                 lastToken.text += '\n' + cap[0].trimRight();
31897                 lastToken.raw += '\n' + cap[0];
31898                 return lastToken;
31899               } else {
31900                 var text = cap[0].replace(/^ {4}/gm, '');
31901                 return {
31902                   type: 'code',
31903                   raw: cap[0],
31904                   codeBlockStyle: 'indented',
31905                   text: !this.options.pedantic
31906                     ? rtrim$1(text, '\n')
31907                     : text
31908                 };
31909               }
31910             }
31911           };
31912
31913           Tokenizer.prototype.fences = function fences (src) {
31914             var cap = this.rules.block.fences.exec(src);
31915             if (cap) {
31916               return {
31917                 type: 'code',
31918                 raw: cap[0],
31919                 lang: cap[2] ? cap[2].trim() : cap[2],
31920                 text: cap[3] || ''
31921               };
31922             }
31923           };
31924
31925           Tokenizer.prototype.heading = function heading (src) {
31926             var cap = this.rules.block.heading.exec(src);
31927             if (cap) {
31928               return {
31929                 type: 'heading',
31930                 raw: cap[0],
31931                 depth: cap[1].length,
31932                 text: cap[2]
31933               };
31934             }
31935           };
31936
31937           Tokenizer.prototype.nptable = function nptable (src) {
31938             var cap = this.rules.block.nptable.exec(src);
31939             if (cap) {
31940               var item = {
31941                 type: 'table',
31942                 header: splitCells$1(cap[1].replace(/^ *| *\| *$/g, '')),
31943                 align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
31944                 cells: cap[3] ? cap[3].replace(/\n$/, '').split('\n') : [],
31945                 raw: cap[0]
31946               };
31947
31948               if (item.header.length === item.align.length) {
31949                 var l = item.align.length;
31950                 var i;
31951                 for (i = 0; i < l; i++) {
31952                   if (/^ *-+: *$/.test(item.align[i])) {
31953                     item.align[i] = 'right';
31954                   } else if (/^ *:-+: *$/.test(item.align[i])) {
31955                     item.align[i] = 'center';
31956                   } else if (/^ *:-+ *$/.test(item.align[i])) {
31957                     item.align[i] = 'left';
31958                   } else {
31959                     item.align[i] = null;
31960                   }
31961                 }
31962
31963                 l = item.cells.length;
31964                 for (i = 0; i < l; i++) {
31965                   item.cells[i] = splitCells$1(item.cells[i], item.header.length);
31966                 }
31967
31968                 return item;
31969               }
31970             }
31971           };
31972
31973           Tokenizer.prototype.hr = function hr (src) {
31974             var cap = this.rules.block.hr.exec(src);
31975             if (cap) {
31976               return {
31977                 type: 'hr',
31978                 raw: cap[0]
31979               };
31980             }
31981           };
31982
31983           Tokenizer.prototype.blockquote = function blockquote (src) {
31984             var cap = this.rules.block.blockquote.exec(src);
31985             if (cap) {
31986               var text = cap[0].replace(/^ *> ?/gm, '');
31987
31988               return {
31989                 type: 'blockquote',
31990                 raw: cap[0],
31991                 text: text
31992               };
31993             }
31994           };
31995
31996           Tokenizer.prototype.list = function list (src) {
31997             var cap = this.rules.block.list.exec(src);
31998             if (cap) {
31999               var raw = cap[0];
32000               var bull = cap[2];
32001               var isordered = bull.length > 1;
32002
32003               var list = {
32004                 type: 'list',
32005                 raw: raw,
32006                 ordered: isordered,
32007                 start: isordered ? +bull : '',
32008                 loose: false,
32009                 items: []
32010               };
32011
32012               // Get each top-level item.
32013               var itemMatch = cap[0].match(this.rules.block.item);
32014
32015               var next = false,
32016                 item,
32017                 space,
32018                 b,
32019                 addBack,
32020                 loose,
32021                 istask,
32022                 ischecked;
32023
32024               var l = itemMatch.length;
32025               for (var i = 0; i < l; i++) {
32026                 item = itemMatch[i];
32027                 raw = item;
32028
32029                 // Remove the list item's bullet
32030                 // so it is seen as the next token.
32031                 space = item.length;
32032                 item = item.replace(/^ *([*+-]|\d+\.) */, '');
32033
32034                 // Outdent whatever the
32035                 // list item contains. Hacky.
32036                 if (~item.indexOf('\n ')) {
32037                   space -= item.length;
32038                   item = !this.options.pedantic
32039                     ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
32040                     : item.replace(/^ {1,4}/gm, '');
32041                 }
32042
32043                 // Determine whether the next list item belongs here.
32044                 // Backpedal if it does not belong in this list.
32045                 if (i !== l - 1) {
32046                   b = this.rules.block.bullet.exec(itemMatch[i + 1])[0];
32047                   if (bull.length > 1 ? b.length === 1
32048                     : (b.length > 1 || (this.options.smartLists && b !== bull))) {
32049                     addBack = itemMatch.slice(i + 1).join('\n');
32050                     list.raw = list.raw.substring(0, list.raw.length - addBack.length);
32051                     i = l - 1;
32052                   }
32053                 }
32054
32055                 // Determine whether item is loose or not.
32056                 // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
32057                 // for discount behavior.
32058                 loose = next || /\n\n(?!\s*$)/.test(item);
32059                 if (i !== l - 1) {
32060                   next = item.charAt(item.length - 1) === '\n';
32061                   if (!loose) { loose = next; }
32062                 }
32063
32064                 if (loose) {
32065                   list.loose = true;
32066                 }
32067
32068                 // Check for task list items
32069                 istask = /^\[[ xX]\] /.test(item);
32070                 ischecked = undefined;
32071                 if (istask) {
32072                   ischecked = item[1] !== ' ';
32073                   item = item.replace(/^\[[ xX]\] +/, '');
32074                 }
32075
32076                 list.items.push({
32077                   raw: raw,
32078                   task: istask,
32079                   checked: ischecked,
32080                   loose: loose,
32081                   text: item
32082                 });
32083               }
32084
32085               return list;
32086             }
32087           };
32088
32089           Tokenizer.prototype.html = function html (src) {
32090             var cap = this.rules.block.html.exec(src);
32091             if (cap) {
32092               return {
32093                 type: this.options.sanitize
32094                   ? 'paragraph'
32095                   : 'html',
32096                 raw: cap[0],
32097                 pre: !this.options.sanitizer
32098                   && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
32099                 text: this.options.sanitize ? (this.options.sanitizer ? this.options.sanitizer(cap[0]) : escape$2(cap[0])) : cap[0]
32100               };
32101             }
32102           };
32103
32104           Tokenizer.prototype.def = function def (src) {
32105             var cap = this.rules.block.def.exec(src);
32106             if (cap) {
32107               if (cap[3]) { cap[3] = cap[3].substring(1, cap[3].length - 1); }
32108               var tag = cap[1].toLowerCase().replace(/\s+/g, ' ');
32109               return {
32110                 tag: tag,
32111                 raw: cap[0],
32112                 href: cap[2],
32113                 title: cap[3]
32114               };
32115             }
32116           };
32117
32118           Tokenizer.prototype.table = function table (src) {
32119             var cap = this.rules.block.table.exec(src);
32120             if (cap) {
32121               var item = {
32122                 type: 'table',
32123                 header: splitCells$1(cap[1].replace(/^ *| *\| *$/g, '')),
32124                 align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
32125                 cells: cap[3] ? cap[3].replace(/\n$/, '').split('\n') : []
32126               };
32127
32128               if (item.header.length === item.align.length) {
32129                 item.raw = cap[0];
32130
32131                 var l = item.align.length;
32132                 var i;
32133                 for (i = 0; i < l; i++) {
32134                   if (/^ *-+: *$/.test(item.align[i])) {
32135                     item.align[i] = 'right';
32136                   } else if (/^ *:-+: *$/.test(item.align[i])) {
32137                     item.align[i] = 'center';
32138                   } else if (/^ *:-+ *$/.test(item.align[i])) {
32139                     item.align[i] = 'left';
32140                   } else {
32141                     item.align[i] = null;
32142                   }
32143                 }
32144
32145                 l = item.cells.length;
32146                 for (i = 0; i < l; i++) {
32147                   item.cells[i] = splitCells$1(
32148                     item.cells[i].replace(/^ *\| *| *\| *$/g, ''),
32149                     item.header.length);
32150                 }
32151
32152                 return item;
32153               }
32154             }
32155           };
32156
32157           Tokenizer.prototype.lheading = function lheading (src) {
32158             var cap = this.rules.block.lheading.exec(src);
32159             if (cap) {
32160               return {
32161                 type: 'heading',
32162                 raw: cap[0],
32163                 depth: cap[2].charAt(0) === '=' ? 1 : 2,
32164                 text: cap[1]
32165               };
32166             }
32167           };
32168
32169           Tokenizer.prototype.paragraph = function paragraph (src) {
32170             var cap = this.rules.block.paragraph.exec(src);
32171             if (cap) {
32172               return {
32173                 type: 'paragraph',
32174                 raw: cap[0],
32175                 text: cap[1].charAt(cap[1].length - 1) === '\n'
32176                   ? cap[1].slice(0, -1)
32177                   : cap[1]
32178               };
32179             }
32180           };
32181
32182           Tokenizer.prototype.text = function text (src) {
32183             var cap = this.rules.block.text.exec(src);
32184             if (cap) {
32185               return {
32186                 type: 'text',
32187                 raw: cap[0],
32188                 text: cap[0]
32189               };
32190             }
32191           };
32192
32193           Tokenizer.prototype.escape = function escape$1 (src) {
32194             var cap = this.rules.inline.escape.exec(src);
32195             if (cap) {
32196               return {
32197                 type: 'escape',
32198                 raw: cap[0],
32199                 text: escape$2(cap[1])
32200               };
32201             }
32202           };
32203
32204           Tokenizer.prototype.tag = function tag (src, inLink, inRawBlock) {
32205             var cap = this.rules.inline.tag.exec(src);
32206             if (cap) {
32207               if (!inLink && /^<a /i.test(cap[0])) {
32208                 inLink = true;
32209               } else if (inLink && /^<\/a>/i.test(cap[0])) {
32210                 inLink = false;
32211               }
32212               if (!inRawBlock && /^<(pre|code|kbd|script)(\s|>)/i.test(cap[0])) {
32213                 inRawBlock = true;
32214               } else if (inRawBlock && /^<\/(pre|code|kbd|script)(\s|>)/i.test(cap[0])) {
32215                 inRawBlock = false;
32216               }
32217
32218               return {
32219                 type: this.options.sanitize
32220                   ? 'text'
32221                   : 'html',
32222                 raw: cap[0],
32223                 inLink: inLink,
32224                 inRawBlock: inRawBlock,
32225                 text: this.options.sanitize
32226                   ? (this.options.sanitizer
32227                     ? this.options.sanitizer(cap[0])
32228                     : escape$2(cap[0]))
32229                   : cap[0]
32230               };
32231             }
32232           };
32233
32234           Tokenizer.prototype.link = function link (src) {
32235             var cap = this.rules.inline.link.exec(src);
32236             if (cap) {
32237               var lastParenIndex = findClosingBracket$1(cap[2], '()');
32238               if (lastParenIndex > -1) {
32239                 var start = cap[0].indexOf('!') === 0 ? 5 : 4;
32240                 var linkLen = start + cap[1].length + lastParenIndex;
32241                 cap[2] = cap[2].substring(0, lastParenIndex);
32242                 cap[0] = cap[0].substring(0, linkLen).trim();
32243                 cap[3] = '';
32244               }
32245               var href = cap[2];
32246               var title = '';
32247               if (this.options.pedantic) {
32248                 var link = /^([^'"]*[^\s])\s+(['"])(.*)\2/.exec(href);
32249
32250                 if (link) {
32251                   href = link[1];
32252                   title = link[3];
32253                 } else {
32254                   title = '';
32255                 }
32256               } else {
32257                 title = cap[3] ? cap[3].slice(1, -1) : '';
32258               }
32259               href = href.trim().replace(/^<([\s\S]*)>$/, '$1');
32260               var token = outputLink(cap, {
32261                 href: href ? href.replace(this.rules.inline._escapes, '$1') : href,
32262                 title: title ? title.replace(this.rules.inline._escapes, '$1') : title
32263               }, cap[0]);
32264               return token;
32265             }
32266           };
32267
32268           Tokenizer.prototype.reflink = function reflink (src, links) {
32269             var cap;
32270             if ((cap = this.rules.inline.reflink.exec(src))
32271                 || (cap = this.rules.inline.nolink.exec(src))) {
32272               var link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
32273               link = links[link.toLowerCase()];
32274               if (!link || !link.href) {
32275                 var text = cap[0].charAt(0);
32276                 return {
32277                   type: 'text',
32278                   raw: text,
32279                   text: text
32280                 };
32281               }
32282               var token = outputLink(cap, link, cap[0]);
32283               return token;
32284             }
32285           };
32286
32287           Tokenizer.prototype.strong = function strong (src) {
32288             var cap = this.rules.inline.strong.exec(src);
32289             if (cap) {
32290               return {
32291                 type: 'strong',
32292                 raw: cap[0],
32293                 text: cap[4] || cap[3] || cap[2] || cap[1]
32294               };
32295             }
32296           };
32297
32298           Tokenizer.prototype.em = function em (src) {
32299             var cap = this.rules.inline.em.exec(src);
32300             if (cap) {
32301               return {
32302                 type: 'em',
32303                 raw: cap[0],
32304                 text: cap[6] || cap[5] || cap[4] || cap[3] || cap[2] || cap[1]
32305               };
32306             }
32307           };
32308
32309           Tokenizer.prototype.codespan = function codespan (src) {
32310             var cap = this.rules.inline.code.exec(src);
32311             if (cap) {
32312               return {
32313                 type: 'codespan',
32314                 raw: cap[0],
32315                 text: escape$2(cap[2].trim(), true)
32316               };
32317             }
32318           };
32319
32320           Tokenizer.prototype.br = function br (src) {
32321             var cap = this.rules.inline.br.exec(src);
32322             if (cap) {
32323               return {
32324                 type: 'br',
32325                 raw: cap[0]
32326               };
32327             }
32328           };
32329
32330           Tokenizer.prototype.del = function del (src) {
32331             var cap = this.rules.inline.del.exec(src);
32332             if (cap) {
32333               return {
32334                 type: 'del',
32335                 raw: cap[0],
32336                 text: cap[1]
32337               };
32338             }
32339           };
32340
32341           Tokenizer.prototype.autolink = function autolink (src, mangle) {
32342             var cap = this.rules.inline.autolink.exec(src);
32343             if (cap) {
32344               var text, href;
32345               if (cap[2] === '@') {
32346                 text = escape$2(this.options.mangle ? mangle(cap[1]) : cap[1]);
32347                 href = 'mailto:' + text;
32348               } else {
32349                 text = escape$2(cap[1]);
32350                 href = text;
32351               }
32352
32353               return {
32354                 type: 'link',
32355                 raw: cap[0],
32356                 text: text,
32357                 href: href,
32358                 tokens: [
32359                   {
32360                     type: 'text',
32361                     raw: text,
32362                     text: text
32363                   }
32364                 ]
32365               };
32366             }
32367           };
32368
32369           Tokenizer.prototype.url = function url (src, mangle) {
32370             var cap;
32371             if (cap = this.rules.inline.url.exec(src)) {
32372               var text, href;
32373               if (cap[2] === '@') {
32374                 text = escape$2(this.options.mangle ? mangle(cap[0]) : cap[0]);
32375                 href = 'mailto:' + text;
32376               } else {
32377                 // do extended autolink path validation
32378                 var prevCapZero;
32379                 do {
32380                   prevCapZero = cap[0];
32381                   cap[0] = this.rules.inline._backpedal.exec(cap[0])[0];
32382                 } while (prevCapZero !== cap[0]);
32383                 text = escape$2(cap[0]);
32384                 if (cap[1] === 'www.') {
32385                   href = 'http://' + text;
32386                 } else {
32387                   href = text;
32388                 }
32389               }
32390               return {
32391                 type: 'link',
32392                 raw: cap[0],
32393                 text: text,
32394                 href: href,
32395                 tokens: [
32396                   {
32397                     type: 'text',
32398                     raw: text,
32399                     text: text
32400                   }
32401                 ]
32402               };
32403             }
32404           };
32405
32406           Tokenizer.prototype.inlineText = function inlineText (src, inRawBlock, smartypants) {
32407             var cap = this.rules.inline.text.exec(src);
32408             if (cap) {
32409               var text;
32410               if (inRawBlock) {
32411                 text = this.options.sanitize ? (this.options.sanitizer ? this.options.sanitizer(cap[0]) : escape$2(cap[0])) : cap[0];
32412               } else {
32413                 text = escape$2(this.options.smartypants ? smartypants(cap[0]) : cap[0]);
32414               }
32415               return {
32416                 type: 'text',
32417                 raw: cap[0],
32418                 text: text
32419               };
32420             }
32421           };
32422
32423           return Tokenizer;
32424         }());
32425
32426         var noopTest$1 = helpers.noopTest;
32427         var edit$1 = helpers.edit;
32428         var merge$2 = helpers.merge;
32429
32430         /**
32431          * Block-Level Grammar
32432          */
32433         var block = {
32434           newline: /^\n+/,
32435           code: /^( {4}[^\n]+\n*)+/,
32436           fences: /^ {0,3}(`{3,}(?=[^`\n]*\n)|~{3,})([^\n]*)\n(?:|([\s\S]*?)\n)(?: {0,3}\1[~`]* *(?:\n+|$)|$)/,
32437           hr: /^ {0,3}((?:- *){3,}|(?:_ *){3,}|(?:\* *){3,})(?:\n+|$)/,
32438           heading: /^ {0,3}(#{1,6}) +([^\n]*?)(?: +#+)? *(?:\n+|$)/,
32439           blockquote: /^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/,
32440           list: /^( {0,3})(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
32441           html: '^ {0,3}(?:' // optional indentation
32442             + '<(script|pre|style)[\\s>][\\s\\S]*?(?:</\\1>[^\\n]*\\n+|$)' // (1)
32443             + '|comment[^\\n]*(\\n+|$)' // (2)
32444             + '|<\\?[\\s\\S]*?\\?>\\n*' // (3)
32445             + '|<![A-Z][\\s\\S]*?>\\n*' // (4)
32446             + '|<!\\[CDATA\\[[\\s\\S]*?\\]\\]>\\n*' // (5)
32447             + '|</?(tag)(?: +|\\n|/?>)[\\s\\S]*?(?:\\n{2,}|$)' // (6)
32448             + '|<(?!script|pre|style)([a-z][\\w-]*)(?:attribute)*? */?>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:\\n{2,}|$)' // (7) open tag
32449             + '|</(?!script|pre|style)[a-z][\\w-]*\\s*>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:\\n{2,}|$)' // (7) closing tag
32450             + ')',
32451           def: /^ {0,3}\[(label)\]: *\n? *<?([^\s>]+)>?(?:(?: +\n? *| *\n *)(title))? *(?:\n+|$)/,
32452           nptable: noopTest$1,
32453           table: noopTest$1,
32454           lheading: /^([^\n]+)\n {0,3}(=+|-+) *(?:\n+|$)/,
32455           // regex template, placeholders will be replaced according to different paragraph
32456           // interruption rules of commonmark and the original markdown spec:
32457           _paragraph: /^([^\n]+(?:\n(?!hr|heading|lheading|blockquote|fences|list|html)[^\n]+)*)/,
32458           text: /^[^\n]+/
32459         };
32460
32461         block._label = /(?!\s*\])(?:\\[\[\]]|[^\[\]])+/;
32462         block._title = /(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/;
32463         block.def = edit$1(block.def)
32464           .replace('label', block._label)
32465           .replace('title', block._title)
32466           .getRegex();
32467
32468         block.bullet = /(?:[*+-]|\d{1,9}\.)/;
32469         block.item = /^( *)(bull) ?[^\n]*(?:\n(?!\1bull ?)[^\n]*)*/;
32470         block.item = edit$1(block.item, 'gm')
32471           .replace(/bull/g, block.bullet)
32472           .getRegex();
32473
32474         block.list = edit$1(block.list)
32475           .replace(/bull/g, block.bullet)
32476           .replace('hr', '\\n+(?=\\1?(?:(?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$))')
32477           .replace('def', '\\n+(?=' + block.def.source + ')')
32478           .getRegex();
32479
32480         block._tag = 'address|article|aside|base|basefont|blockquote|body|caption'
32481           + '|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption'
32482           + '|figure|footer|form|frame|frameset|h[1-6]|head|header|hr|html|iframe'
32483           + '|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option'
32484           + '|p|param|section|source|summary|table|tbody|td|tfoot|th|thead|title|tr'
32485           + '|track|ul';
32486         block._comment = /<!--(?!-?>)[\s\S]*?-->/;
32487         block.html = edit$1(block.html, 'i')
32488           .replace('comment', block._comment)
32489           .replace('tag', block._tag)
32490           .replace('attribute', / +[a-zA-Z:_][\w.:-]*(?: *= *"[^"\n]*"| *= *'[^'\n]*'| *= *[^\s"'=<>`]+)?/)
32491           .getRegex();
32492
32493         block.paragraph = edit$1(block._paragraph)
32494           .replace('hr', block.hr)
32495           .replace('heading', ' {0,3}#{1,6} ')
32496           .replace('|lheading', '') // setex headings don't interrupt commonmark paragraphs
32497           .replace('blockquote', ' {0,3}>')
32498           .replace('fences', ' {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n')
32499           .replace('list', ' {0,3}(?:[*+-]|1[.)]) ') // only lists starting from 1 can interrupt
32500           .replace('html', '</?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|!--)')
32501           .replace('tag', block._tag) // pars can be interrupted by type (6) html blocks
32502           .getRegex();
32503
32504         block.blockquote = edit$1(block.blockquote)
32505           .replace('paragraph', block.paragraph)
32506           .getRegex();
32507
32508         /**
32509          * Normal Block Grammar
32510          */
32511
32512         block.normal = merge$2({}, block);
32513
32514         /**
32515          * GFM Block Grammar
32516          */
32517
32518         block.gfm = merge$2({}, block.normal, {
32519           nptable: '^ *([^|\\n ].*\\|.*)\\n' // Header
32520             + ' *([-:]+ *\\|[-| :]*)' // Align
32521             + '(?:\\n((?:(?!\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)', // Cells
32522           table: '^ *\\|(.+)\\n' // Header
32523             + ' *\\|?( *[-:]+[-| :]*)' // Align
32524             + '(?:\\n *((?:(?!\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)' // Cells
32525         });
32526
32527         block.gfm.nptable = edit$1(block.gfm.nptable)
32528           .replace('hr', block.hr)
32529           .replace('heading', ' {0,3}#{1,6} ')
32530           .replace('blockquote', ' {0,3}>')
32531           .replace('code', ' {4}[^\\n]')
32532           .replace('fences', ' {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n')
32533           .replace('list', ' {0,3}(?:[*+-]|1[.)]) ') // only lists starting from 1 can interrupt
32534           .replace('html', '</?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|!--)')
32535           .replace('tag', block._tag) // tables can be interrupted by type (6) html blocks
32536           .getRegex();
32537
32538         block.gfm.table = edit$1(block.gfm.table)
32539           .replace('hr', block.hr)
32540           .replace('heading', ' {0,3}#{1,6} ')
32541           .replace('blockquote', ' {0,3}>')
32542           .replace('code', ' {4}[^\\n]')
32543           .replace('fences', ' {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n')
32544           .replace('list', ' {0,3}(?:[*+-]|1[.)]) ') // only lists starting from 1 can interrupt
32545           .replace('html', '</?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|!--)')
32546           .replace('tag', block._tag) // tables can be interrupted by type (6) html blocks
32547           .getRegex();
32548
32549         /**
32550          * Pedantic grammar (original John Gruber's loose markdown specification)
32551          */
32552
32553         block.pedantic = merge$2({}, block.normal, {
32554           html: edit$1(
32555             '^ *(?:comment *(?:\\n|\\s*$)'
32556             + '|<(tag)[\\s\\S]+?</\\1> *(?:\\n{2,}|\\s*$)' // closed tag
32557             + '|<tag(?:"[^"]*"|\'[^\']*\'|\\s[^\'"/>\\s]*)*?/?> *(?:\\n{2,}|\\s*$))')
32558             .replace('comment', block._comment)
32559             .replace(/tag/g, '(?!(?:'
32560               + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub'
32561               + '|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)'
32562               + '\\b)\\w+(?!:|[^\\w\\s@]*@)\\b')
32563             .getRegex(),
32564           def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +(["(][^\n]+[")]))? *(?:\n+|$)/,
32565           heading: /^ *(#{1,6}) *([^\n]+?) *(?:#+ *)?(?:\n+|$)/,
32566           fences: noopTest$1, // fences not supported
32567           paragraph: edit$1(block.normal._paragraph)
32568             .replace('hr', block.hr)
32569             .replace('heading', ' *#{1,6} *[^\n]')
32570             .replace('lheading', block.lheading)
32571             .replace('blockquote', ' {0,3}>')
32572             .replace('|fences', '')
32573             .replace('|list', '')
32574             .replace('|html', '')
32575             .getRegex()
32576         });
32577
32578         /**
32579          * Inline-Level Grammar
32580          */
32581         var inline = {
32582           escape: /^\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/,
32583           autolink: /^<(scheme:[^\s\x00-\x1f<>]*|email)>/,
32584           url: noopTest$1,
32585           tag: '^comment'
32586             + '|^</[a-zA-Z][\\w:-]*\\s*>' // self-closing tag
32587             + '|^<[a-zA-Z][\\w-]*(?:attribute)*?\\s*/?>' // open tag
32588             + '|^<\\?[\\s\\S]*?\\?>' // processing instruction, e.g. <?php ?>
32589             + '|^<![a-zA-Z]+\\s[\\s\\S]*?>' // declaration, e.g. <!DOCTYPE html>
32590             + '|^<!\\[CDATA\\[[\\s\\S]*?\\]\\]>', // CDATA section
32591           link: /^!?\[(label)\]\(\s*(href)(?:\s+(title))?\s*\)/,
32592           reflink: /^!?\[(label)\]\[(?!\s*\])((?:\\[\[\]]?|[^\[\]\\])+)\]/,
32593           nolink: /^!?\[(?!\s*\])((?:\[[^\[\]]*\]|\\[\[\]]|[^\[\]])*)\](?:\[\])?/,
32594           strong: /^__([^\s_])__(?!_)|^\*\*([^\s*])\*\*(?!\*)|^__([^\s][\s\S]*?[^\s])__(?!_)|^\*\*([^\s][\s\S]*?[^\s])\*\*(?!\*)/,
32595           em: /^_([^\s_])_(?!_)|^_([^\s_<][\s\S]*?[^\s_])_(?!_|[^\spunctuation])|^_([^\s_<][\s\S]*?[^\s])_(?!_|[^\spunctuation])|^\*([^\s*<\[])\*(?!\*)|^\*([^\s<"][\s\S]*?[^\s\[\*])\*(?![\]`punctuation])|^\*([^\s*"<\[][\s\S]*[^\s])\*(?!\*)/,
32596           code: /^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/,
32597           br: /^( {2,}|\\)\n(?!\s*$)/,
32598           del: noopTest$1,
32599           text: /^(`+|[^`])(?:[\s\S]*?(?:(?=[\\<!\[`*]|\b_|$)|[^ ](?= {2,}\n))|(?= {2,}\n))/
32600         };
32601
32602         // list of punctuation marks from common mark spec
32603         // without ` and ] to workaround Rule 17 (inline code blocks/links)
32604         inline._punctuation = '!"#$%&\'()*+\\-./:;<=>?@\\[^_{|}~';
32605         inline.em = edit$1(inline.em).replace(/punctuation/g, inline._punctuation).getRegex();
32606
32607         inline._escapes = /\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/g;
32608
32609         inline._scheme = /[a-zA-Z][a-zA-Z0-9+.-]{1,31}/;
32610         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])?)+(?![-_])/;
32611         inline.autolink = edit$1(inline.autolink)
32612           .replace('scheme', inline._scheme)
32613           .replace('email', inline._email)
32614           .getRegex();
32615
32616         inline._attribute = /\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*"[^"]*"|\s*=\s*'[^']*'|\s*=\s*[^\s"'=<>`]+)?/;
32617
32618         inline.tag = edit$1(inline.tag)
32619           .replace('comment', block._comment)
32620           .replace('attribute', inline._attribute)
32621           .getRegex();
32622
32623         inline._label = /(?:\[[^\[\]]*\]|\\.|`[^`]*`|[^\[\]\\`])*?/;
32624         inline._href = /<(?:\\[<>]?|[^\s<>\\])*>|[^\s\x00-\x1f]*/;
32625         inline._title = /"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/;
32626
32627         inline.link = edit$1(inline.link)
32628           .replace('label', inline._label)
32629           .replace('href', inline._href)
32630           .replace('title', inline._title)
32631           .getRegex();
32632
32633         inline.reflink = edit$1(inline.reflink)
32634           .replace('label', inline._label)
32635           .getRegex();
32636
32637         /**
32638          * Normal Inline Grammar
32639          */
32640
32641         inline.normal = merge$2({}, inline);
32642
32643         /**
32644          * Pedantic Inline Grammar
32645          */
32646
32647         inline.pedantic = merge$2({}, inline.normal, {
32648           strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
32649           em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/,
32650           link: edit$1(/^!?\[(label)\]\((.*?)\)/)
32651             .replace('label', inline._label)
32652             .getRegex(),
32653           reflink: edit$1(/^!?\[(label)\]\s*\[([^\]]*)\]/)
32654             .replace('label', inline._label)
32655             .getRegex()
32656         });
32657
32658         /**
32659          * GFM Inline Grammar
32660          */
32661
32662         inline.gfm = merge$2({}, inline.normal, {
32663           escape: edit$1(inline.escape).replace('])', '~|])').getRegex(),
32664           _extended_email: /[A-Za-z0-9._+-]+(@)[a-zA-Z0-9-_]+(?:\.[a-zA-Z0-9-_]*[a-zA-Z0-9])+(?![-_])/,
32665           url: /^((?:ftp|https?):\/\/|www\.)(?:[a-zA-Z0-9\-]+\.?)+[^\s<]*|^email/,
32666           _backpedal: /(?:[^?!.,:;*_~()&]+|\([^)]*\)|&(?![a-zA-Z0-9]+;$)|[?!.,:;*_~)]+(?!$))+/,
32667           del: /^~+(?=\S)([\s\S]*?\S)~+/,
32668           text: /^(`+|[^`])(?:[\s\S]*?(?:(?=[\\<!\[`*~]|\b_|https?:\/\/|ftp:\/\/|www\.|$)|[^ ](?= {2,}\n)|[^a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-](?=[a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-]+@))|(?= {2,}\n|[a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-]+@))/
32669         });
32670
32671         inline.gfm.url = edit$1(inline.gfm.url, 'i')
32672           .replace('email', inline.gfm._extended_email)
32673           .getRegex();
32674         /**
32675          * GFM + Line Breaks Inline Grammar
32676          */
32677
32678         inline.breaks = merge$2({}, inline.gfm, {
32679           br: edit$1(inline.br).replace('{2,}', '*').getRegex(),
32680           text: edit$1(inline.gfm.text)
32681             .replace('\\b_', '\\b_| {2,}\\n')
32682             .replace(/\{2,\}/g, '*')
32683             .getRegex()
32684         });
32685
32686         var rules = {
32687           block: block,
32688           inline: inline
32689         };
32690
32691         var defaults$2 = defaults.defaults;
32692         var block$1 = rules.block;
32693         var inline$1 = rules.inline;
32694
32695         /**
32696          * smartypants text replacement
32697          */
32698         function smartypants(text) {
32699           return text
32700             // em-dashes
32701             .replace(/---/g, '\u2014')
32702             // en-dashes
32703             .replace(/--/g, '\u2013')
32704             // opening singles
32705             .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
32706             // closing singles & apostrophes
32707             .replace(/'/g, '\u2019')
32708             // opening doubles
32709             .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
32710             // closing doubles
32711             .replace(/"/g, '\u201d')
32712             // ellipses
32713             .replace(/\.{3}/g, '\u2026');
32714         }
32715
32716         /**
32717          * mangle email addresses
32718          */
32719         function mangle(text) {
32720           var out = '',
32721             i,
32722             ch;
32723
32724           var l = text.length;
32725           for (i = 0; i < l; i++) {
32726             ch = text.charCodeAt(i);
32727             if (Math.random() > 0.5) {
32728               ch = 'x' + ch.toString(16);
32729             }
32730             out += '&#' + ch + ';';
32731           }
32732
32733           return out;
32734         }
32735
32736         /**
32737          * Block Lexer
32738          */
32739         var Lexer_1 = /*@__PURE__*/(function () {
32740           function Lexer(options) {
32741             this.tokens = [];
32742             this.tokens.links = Object.create(null);
32743             this.options = options || defaults$2;
32744             this.options.tokenizer = this.options.tokenizer || new Tokenizer_1();
32745             this.tokenizer = this.options.tokenizer;
32746             this.tokenizer.options = this.options;
32747
32748             var rules = {
32749               block: block$1.normal,
32750               inline: inline$1.normal
32751             };
32752
32753             if (this.options.pedantic) {
32754               rules.block = block$1.pedantic;
32755               rules.inline = inline$1.pedantic;
32756             } else if (this.options.gfm) {
32757               rules.block = block$1.gfm;
32758               if (this.options.breaks) {
32759                 rules.inline = inline$1.breaks;
32760               } else {
32761                 rules.inline = inline$1.gfm;
32762               }
32763             }
32764             this.tokenizer.rules = rules;
32765           }
32766
32767           var staticAccessors = { rules: { configurable: true } };
32768
32769           /**
32770            * Expose Rules
32771            */
32772           staticAccessors.rules.get = function () {
32773             return {
32774               block: block$1,
32775               inline: inline$1
32776             };
32777           };
32778
32779           /**
32780            * Static Lex Method
32781            */
32782           Lexer.lex = function lex (src, options) {
32783             var lexer = new Lexer(options);
32784             return lexer.lex(src);
32785           };
32786
32787           /**
32788            * Preprocessing
32789            */
32790           Lexer.prototype.lex = function lex (src) {
32791             src = src
32792               .replace(/\r\n|\r/g, '\n')
32793               .replace(/\t/g, '    ');
32794
32795             this.blockTokens(src, this.tokens, true);
32796
32797             this.inline(this.tokens);
32798
32799             return this.tokens;
32800           };
32801
32802           /**
32803            * Lexing
32804            */
32805           Lexer.prototype.blockTokens = function blockTokens (src, tokens, top) {
32806             if ( tokens === void 0 ) tokens = [];
32807             if ( top === void 0 ) top = true;
32808
32809             src = src.replace(/^ +$/gm, '');
32810             var token, i, l;
32811
32812             while (src) {
32813               // newline
32814               if (token = this.tokenizer.space(src)) {
32815                 src = src.substring(token.raw.length);
32816                 if (token.type) {
32817                   tokens.push(token);
32818                 }
32819                 continue;
32820               }
32821
32822               // code
32823               if (token = this.tokenizer.code(src, tokens)) {
32824                 src = src.substring(token.raw.length);
32825                 tokens.push(token);
32826                 continue;
32827               }
32828
32829               // fences
32830               if (token = this.tokenizer.fences(src)) {
32831                 src = src.substring(token.raw.length);
32832                 tokens.push(token);
32833                 continue;
32834               }
32835
32836               // heading
32837               if (token = this.tokenizer.heading(src)) {
32838                 src = src.substring(token.raw.length);
32839                 tokens.push(token);
32840                 continue;
32841               }
32842
32843               // table no leading pipe (gfm)
32844               if (token = this.tokenizer.nptable(src)) {
32845                 src = src.substring(token.raw.length);
32846                 tokens.push(token);
32847                 continue;
32848               }
32849
32850               // hr
32851               if (token = this.tokenizer.hr(src)) {
32852                 src = src.substring(token.raw.length);
32853                 tokens.push(token);
32854                 continue;
32855               }
32856
32857               // blockquote
32858               if (token = this.tokenizer.blockquote(src)) {
32859                 src = src.substring(token.raw.length);
32860                 token.tokens = this.blockTokens(token.text, [], top);
32861                 tokens.push(token);
32862                 continue;
32863               }
32864
32865               // list
32866               if (token = this.tokenizer.list(src)) {
32867                 src = src.substring(token.raw.length);
32868                 l = token.items.length;
32869                 for (i = 0; i < l; i++) {
32870                   token.items[i].tokens = this.blockTokens(token.items[i].text, [], false);
32871                 }
32872                 tokens.push(token);
32873                 continue;
32874               }
32875
32876               // html
32877               if (token = this.tokenizer.html(src)) {
32878                 src = src.substring(token.raw.length);
32879                 tokens.push(token);
32880                 continue;
32881               }
32882
32883               // def
32884               if (top && (token = this.tokenizer.def(src))) {
32885                 src = src.substring(token.raw.length);
32886                 if (!this.tokens.links[token.tag]) {
32887                   this.tokens.links[token.tag] = {
32888                     href: token.href,
32889                     title: token.title
32890                   };
32891                 }
32892                 continue;
32893               }
32894
32895               // table (gfm)
32896               if (token = this.tokenizer.table(src)) {
32897                 src = src.substring(token.raw.length);
32898                 tokens.push(token);
32899                 continue;
32900               }
32901
32902               // lheading
32903               if (token = this.tokenizer.lheading(src)) {
32904                 src = src.substring(token.raw.length);
32905                 tokens.push(token);
32906                 continue;
32907               }
32908
32909               // top-level paragraph
32910               if (top && (token = this.tokenizer.paragraph(src))) {
32911                 src = src.substring(token.raw.length);
32912                 tokens.push(token);
32913                 continue;
32914               }
32915
32916               // text
32917               if (token = this.tokenizer.text(src)) {
32918                 src = src.substring(token.raw.length);
32919                 tokens.push(token);
32920                 continue;
32921               }
32922
32923               if (src) {
32924                 var errMsg = 'Infinite loop on byte: ' + src.charCodeAt(0);
32925                 if (this.options.silent) {
32926                   console.error(errMsg);
32927                   break;
32928                 } else {
32929                   throw new Error(errMsg);
32930                 }
32931               }
32932             }
32933
32934             return tokens;
32935           };
32936
32937           Lexer.prototype.inline = function inline (tokens) {
32938             var i,
32939               j,
32940               k,
32941               l2,
32942               row,
32943               token;
32944
32945             var l = tokens.length;
32946             for (i = 0; i < l; i++) {
32947               token = tokens[i];
32948               switch (token.type) {
32949                 case 'paragraph':
32950                 case 'text':
32951                 case 'heading': {
32952                   token.tokens = [];
32953                   this.inlineTokens(token.text, token.tokens);
32954                   break;
32955                 }
32956                 case 'table': {
32957                   token.tokens = {
32958                     header: [],
32959                     cells: []
32960                   };
32961
32962                   // header
32963                   l2 = token.header.length;
32964                   for (j = 0; j < l2; j++) {
32965                     token.tokens.header[j] = [];
32966                     this.inlineTokens(token.header[j], token.tokens.header[j]);
32967                   }
32968
32969                   // cells
32970                   l2 = token.cells.length;
32971                   for (j = 0; j < l2; j++) {
32972                     row = token.cells[j];
32973                     token.tokens.cells[j] = [];
32974                     for (k = 0; k < row.length; k++) {
32975                       token.tokens.cells[j][k] = [];
32976                       this.inlineTokens(row[k], token.tokens.cells[j][k]);
32977                     }
32978                   }
32979
32980                   break;
32981                 }
32982                 case 'blockquote': {
32983                   this.inline(token.tokens);
32984                   break;
32985                 }
32986                 case 'list': {
32987                   l2 = token.items.length;
32988                   for (j = 0; j < l2; j++) {
32989                     this.inline(token.items[j].tokens);
32990                   }
32991                   break;
32992                 }
32993               }
32994             }
32995
32996             return tokens;
32997           };
32998
32999           /**
33000            * Lexing/Compiling
33001            */
33002           Lexer.prototype.inlineTokens = function inlineTokens (src, tokens, inLink, inRawBlock) {
33003             if ( tokens === void 0 ) tokens = [];
33004             if ( inLink === void 0 ) inLink = false;
33005             if ( inRawBlock === void 0 ) inRawBlock = false;
33006
33007             var token;
33008
33009             while (src) {
33010               // escape
33011               if (token = this.tokenizer.escape(src)) {
33012                 src = src.substring(token.raw.length);
33013                 tokens.push(token);
33014                 continue;
33015               }
33016
33017               // tag
33018               if (token = this.tokenizer.tag(src, inLink, inRawBlock)) {
33019                 src = src.substring(token.raw.length);
33020                 inLink = token.inLink;
33021                 inRawBlock = token.inRawBlock;
33022                 tokens.push(token);
33023                 continue;
33024               }
33025
33026               // link
33027               if (token = this.tokenizer.link(src)) {
33028                 src = src.substring(token.raw.length);
33029                 if (token.type === 'link') {
33030                   token.tokens = this.inlineTokens(token.text, [], true, inRawBlock);
33031                 }
33032                 tokens.push(token);
33033                 continue;
33034               }
33035
33036               // reflink, nolink
33037               if (token = this.tokenizer.reflink(src, this.tokens.links)) {
33038                 src = src.substring(token.raw.length);
33039                 if (token.type === 'link') {
33040                   token.tokens = this.inlineTokens(token.text, [], true, inRawBlock);
33041                 }
33042                 tokens.push(token);
33043                 continue;
33044               }
33045
33046               // strong
33047               if (token = this.tokenizer.strong(src)) {
33048                 src = src.substring(token.raw.length);
33049                 token.tokens = this.inlineTokens(token.text, [], inLink, inRawBlock);
33050                 tokens.push(token);
33051                 continue;
33052               }
33053
33054               // em
33055               if (token = this.tokenizer.em(src)) {
33056                 src = src.substring(token.raw.length);
33057                 token.tokens = this.inlineTokens(token.text, [], inLink, inRawBlock);
33058                 tokens.push(token);
33059                 continue;
33060               }
33061
33062               // code
33063               if (token = this.tokenizer.codespan(src)) {
33064                 src = src.substring(token.raw.length);
33065                 tokens.push(token);
33066                 continue;
33067               }
33068
33069               // br
33070               if (token = this.tokenizer.br(src)) {
33071                 src = src.substring(token.raw.length);
33072                 tokens.push(token);
33073                 continue;
33074               }
33075
33076               // del (gfm)
33077               if (token = this.tokenizer.del(src)) {
33078                 src = src.substring(token.raw.length);
33079                 token.tokens = this.inlineTokens(token.text, [], inLink, inRawBlock);
33080                 tokens.push(token);
33081                 continue;
33082               }
33083
33084               // autolink
33085               if (token = this.tokenizer.autolink(src, mangle)) {
33086                 src = src.substring(token.raw.length);
33087                 tokens.push(token);
33088                 continue;
33089               }
33090
33091               // url (gfm)
33092               if (!inLink && (token = this.tokenizer.url(src, mangle))) {
33093                 src = src.substring(token.raw.length);
33094                 tokens.push(token);
33095                 continue;
33096               }
33097
33098               // text
33099               if (token = this.tokenizer.inlineText(src, inRawBlock, smartypants)) {
33100                 src = src.substring(token.raw.length);
33101                 tokens.push(token);
33102                 continue;
33103               }
33104
33105               if (src) {
33106                 var errMsg = 'Infinite loop on byte: ' + src.charCodeAt(0);
33107                 if (this.options.silent) {
33108                   console.error(errMsg);
33109                   break;
33110                 } else {
33111                   throw new Error(errMsg);
33112                 }
33113               }
33114             }
33115
33116             return tokens;
33117           };
33118
33119           Object.defineProperties( Lexer, staticAccessors );
33120
33121           return Lexer;
33122         }());
33123
33124         var defaults$3 = defaults.defaults;
33125         var cleanUrl$1 = helpers.cleanUrl;
33126         var escape$3 = helpers.escape;
33127
33128         /**
33129          * Renderer
33130          */
33131         var Renderer_1 = /*@__PURE__*/(function () {
33132           function Renderer(options) {
33133             this.options = options || defaults$3;
33134           }
33135
33136           Renderer.prototype.code = function code (code$1, infostring, escaped) {
33137             var lang = (infostring || '').match(/\S*/)[0];
33138             if (this.options.highlight) {
33139               var out = this.options.highlight(code$1, lang);
33140               if (out != null && out !== code$1) {
33141                 escaped = true;
33142                 code$1 = out;
33143               }
33144             }
33145
33146             if (!lang) {
33147               return '<pre><code>'
33148                 + (escaped ? code$1 : escape$3(code$1, true))
33149                 + '</code></pre>';
33150             }
33151
33152             return '<pre><code class="'
33153               + this.options.langPrefix
33154               + escape$3(lang, true)
33155               + '">'
33156               + (escaped ? code$1 : escape$3(code$1, true))
33157               + '</code></pre>\n';
33158           };
33159
33160           Renderer.prototype.blockquote = function blockquote (quote) {
33161             return '<blockquote>\n' + quote + '</blockquote>\n';
33162           };
33163
33164           Renderer.prototype.html = function html (html$1) {
33165             return html$1;
33166           };
33167
33168           Renderer.prototype.heading = function heading (text, level, raw, slugger) {
33169             if (this.options.headerIds) {
33170               return '<h'
33171                 + level
33172                 + ' id="'
33173                 + this.options.headerPrefix
33174                 + slugger.slug(raw)
33175                 + '">'
33176                 + text
33177                 + '</h'
33178                 + level
33179                 + '>\n';
33180             }
33181             // ignore IDs
33182             return '<h' + level + '>' + text + '</h' + level + '>\n';
33183           };
33184
33185           Renderer.prototype.hr = function hr () {
33186             return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
33187           };
33188
33189           Renderer.prototype.list = function list (body, ordered, start) {
33190             var type = ordered ? 'ol' : 'ul',
33191               startatt = (ordered && start !== 1) ? (' start="' + start + '"') : '';
33192             return '<' + type + startatt + '>\n' + body + '</' + type + '>\n';
33193           };
33194
33195           Renderer.prototype.listitem = function listitem (text) {
33196             return '<li>' + text + '</li>\n';
33197           };
33198
33199           Renderer.prototype.checkbox = function checkbox (checked) {
33200             return '<input '
33201               + (checked ? 'checked="" ' : '')
33202               + 'disabled="" type="checkbox"'
33203               + (this.options.xhtml ? ' /' : '')
33204               + '> ';
33205           };
33206
33207           Renderer.prototype.paragraph = function paragraph (text) {
33208             return '<p>' + text + '</p>\n';
33209           };
33210
33211           Renderer.prototype.table = function table (header, body) {
33212             if (body) { body = '<tbody>' + body + '</tbody>'; }
33213
33214             return '<table>\n'
33215               + '<thead>\n'
33216               + header
33217               + '</thead>\n'
33218               + body
33219               + '</table>\n';
33220           };
33221
33222           Renderer.prototype.tablerow = function tablerow (content) {
33223             return '<tr>\n' + content + '</tr>\n';
33224           };
33225
33226           Renderer.prototype.tablecell = function tablecell (content, flags) {
33227             var type = flags.header ? 'th' : 'td';
33228             var tag = flags.align
33229               ? '<' + type + ' align="' + flags.align + '">'
33230               : '<' + type + '>';
33231             return tag + content + '</' + type + '>\n';
33232           };
33233
33234           // span level renderer
33235           Renderer.prototype.strong = function strong (text) {
33236             return '<strong>' + text + '</strong>';
33237           };
33238
33239           Renderer.prototype.em = function em (text) {
33240             return '<em>' + text + '</em>';
33241           };
33242
33243           Renderer.prototype.codespan = function codespan (text) {
33244             return '<code>' + text + '</code>';
33245           };
33246
33247           Renderer.prototype.br = function br () {
33248             return this.options.xhtml ? '<br/>' : '<br>';
33249           };
33250
33251           Renderer.prototype.del = function del (text) {
33252             return '<del>' + text + '</del>';
33253           };
33254
33255           Renderer.prototype.link = function link (href, title, text) {
33256             href = cleanUrl$1(this.options.sanitize, this.options.baseUrl, href);
33257             if (href === null) {
33258               return text;
33259             }
33260             var out = '<a href="' + escape$3(href) + '"';
33261             if (title) {
33262               out += ' title="' + title + '"';
33263             }
33264             out += '>' + text + '</a>';
33265             return out;
33266           };
33267
33268           Renderer.prototype.image = function image (href, title, text) {
33269             href = cleanUrl$1(this.options.sanitize, this.options.baseUrl, href);
33270             if (href === null) {
33271               return text;
33272             }
33273
33274             var out = '<img src="' + href + '" alt="' + text + '"';
33275             if (title) {
33276               out += ' title="' + title + '"';
33277             }
33278             out += this.options.xhtml ? '/>' : '>';
33279             return out;
33280           };
33281
33282           Renderer.prototype.text = function text (text$1) {
33283             return text$1;
33284           };
33285
33286           return Renderer;
33287         }());
33288
33289         /**
33290          * TextRenderer
33291          * returns only the textual part of the token
33292          */
33293         var TextRenderer_1 = /*@__PURE__*/(function () {
33294           function TextRenderer () {}
33295
33296           TextRenderer.prototype.strong = function strong (text) {
33297             return text;
33298           };
33299
33300           TextRenderer.prototype.em = function em (text) {
33301             return text;
33302           };
33303
33304           TextRenderer.prototype.codespan = function codespan (text) {
33305             return text;
33306           };
33307
33308           TextRenderer.prototype.del = function del (text) {
33309             return text;
33310           };
33311
33312           TextRenderer.prototype.html = function html (text) {
33313             return text;
33314           };
33315
33316           TextRenderer.prototype.text = function text (text$1) {
33317             return text$1;
33318           };
33319
33320           TextRenderer.prototype.link = function link (href, title, text) {
33321             return '' + text;
33322           };
33323
33324           TextRenderer.prototype.image = function image (href, title, text) {
33325             return '' + text;
33326           };
33327
33328           TextRenderer.prototype.br = function br () {
33329             return '';
33330           };
33331
33332           return TextRenderer;
33333         }());
33334
33335         /**
33336          * Slugger generates header id
33337          */
33338         var Slugger_1 = /*@__PURE__*/(function () {
33339           function Slugger() {
33340             this.seen = {};
33341           }
33342
33343           /**
33344            * Convert string to unique id
33345            */
33346           Slugger.prototype.slug = function slug (value) {
33347             var slug = value
33348               .toLowerCase()
33349               .trim()
33350               // remove html tags
33351               .replace(/<[!\/a-z].*?>/ig, '')
33352               // remove unwanted chars
33353               .replace(/[\u2000-\u206F\u2E00-\u2E7F\\'!"#$%&()*+,./:;<=>?@[\]^`{|}~]/g, '')
33354               .replace(/\s/g, '-');
33355
33356             if (this.seen.hasOwnProperty(slug)) {
33357               var originalSlug = slug;
33358               do {
33359                 this.seen[originalSlug]++;
33360                 slug = originalSlug + '-' + this.seen[originalSlug];
33361               } while (this.seen.hasOwnProperty(slug));
33362             }
33363             this.seen[slug] = 0;
33364
33365             return slug;
33366           };
33367
33368           return Slugger;
33369         }());
33370
33371         var defaults$4 = defaults.defaults;
33372         var unescape$2 = helpers.unescape;
33373
33374         /**
33375          * Parsing & Compiling
33376          */
33377         var Parser_1 = /*@__PURE__*/(function () {
33378           function Parser(options) {
33379             this.options = options || defaults$4;
33380             this.options.renderer = this.options.renderer || new Renderer_1();
33381             this.renderer = this.options.renderer;
33382             this.renderer.options = this.options;
33383             this.textRenderer = new TextRenderer_1();
33384             this.slugger = new Slugger_1();
33385           }
33386
33387           /**
33388            * Static Parse Method
33389            */
33390           Parser.parse = function parse (tokens, options) {
33391             var parser = new Parser(options);
33392             return parser.parse(tokens);
33393           };
33394
33395           /**
33396            * Parse Loop
33397            */
33398           Parser.prototype.parse = function parse (tokens, top) {
33399             if ( top === void 0 ) top = true;
33400
33401             var out = '',
33402               i,
33403               j,
33404               k,
33405               l2,
33406               l3,
33407               row,
33408               cell,
33409               header,
33410               body,
33411               token,
33412               ordered,
33413               start,
33414               loose,
33415               itemBody,
33416               item,
33417               checked,
33418               task,
33419               checkbox;
33420
33421             var l = tokens.length;
33422             for (i = 0; i < l; i++) {
33423               token = tokens[i];
33424               switch (token.type) {
33425                 case 'space': {
33426                   continue;
33427                 }
33428                 case 'hr': {
33429                   out += this.renderer.hr();
33430                   continue;
33431                 }
33432                 case 'heading': {
33433                   out += this.renderer.heading(
33434                     this.parseInline(token.tokens),
33435                     token.depth,
33436                     unescape$2(this.parseInline(token.tokens, this.textRenderer)),
33437                     this.slugger);
33438                   continue;
33439                 }
33440                 case 'code': {
33441                   out += this.renderer.code(token.text,
33442                     token.lang,
33443                     token.escaped);
33444                   continue;
33445                 }
33446                 case 'table': {
33447                   header = '';
33448
33449                   // header
33450                   cell = '';
33451                   l2 = token.header.length;
33452                   for (j = 0; j < l2; j++) {
33453                     cell += this.renderer.tablecell(
33454                       this.parseInline(token.tokens.header[j]),
33455                       { header: true, align: token.align[j] }
33456                     );
33457                   }
33458                   header += this.renderer.tablerow(cell);
33459
33460                   body = '';
33461                   l2 = token.cells.length;
33462                   for (j = 0; j < l2; j++) {
33463                     row = token.tokens.cells[j];
33464
33465                     cell = '';
33466                     l3 = row.length;
33467                     for (k = 0; k < l3; k++) {
33468                       cell += this.renderer.tablecell(
33469                         this.parseInline(row[k]),
33470                         { header: false, align: token.align[k] }
33471                       );
33472                     }
33473
33474                     body += this.renderer.tablerow(cell);
33475                   }
33476                   out += this.renderer.table(header, body);
33477                   continue;
33478                 }
33479                 case 'blockquote': {
33480                   body = this.parse(token.tokens);
33481                   out += this.renderer.blockquote(body);
33482                   continue;
33483                 }
33484                 case 'list': {
33485                   ordered = token.ordered;
33486                   start = token.start;
33487                   loose = token.loose;
33488                   l2 = token.items.length;
33489
33490                   body = '';
33491                   for (j = 0; j < l2; j++) {
33492                     item = token.items[j];
33493                     checked = item.checked;
33494                     task = item.task;
33495
33496                     itemBody = '';
33497                     if (item.task) {
33498                       checkbox = this.renderer.checkbox(checked);
33499                       if (loose) {
33500                         if (item.tokens[0].type === 'text') {
33501                           item.tokens[0].text = checkbox + ' ' + item.tokens[0].text;
33502                           if (item.tokens[0].tokens && item.tokens[0].tokens.length > 0 && item.tokens[0].tokens[0].type === 'text') {
33503                             item.tokens[0].tokens[0].text = checkbox + ' ' + item.tokens[0].tokens[0].text;
33504                           }
33505                         } else {
33506                           item.tokens.unshift({
33507                             type: 'text',
33508                             text: checkbox
33509                           });
33510                         }
33511                       } else {
33512                         itemBody += checkbox;
33513                       }
33514                     }
33515
33516                     itemBody += this.parse(item.tokens, loose);
33517                     body += this.renderer.listitem(itemBody, task, checked);
33518                   }
33519
33520                   out += this.renderer.list(body, ordered, start);
33521                   continue;
33522                 }
33523                 case 'html': {
33524                   // TODO parse inline content if parameter markdown=1
33525                   out += this.renderer.html(token.text);
33526                   continue;
33527                 }
33528                 case 'paragraph': {
33529                   out += this.renderer.paragraph(this.parseInline(token.tokens));
33530                   continue;
33531                 }
33532                 case 'text': {
33533                   body = token.tokens ? this.parseInline(token.tokens) : token.text;
33534                   while (i + 1 < l && tokens[i + 1].type === 'text') {
33535                     token = tokens[++i];
33536                     body += '\n' + (token.tokens ? this.parseInline(token.tokens) : token.text);
33537                   }
33538                   out += top ? this.renderer.paragraph(body) : body;
33539                   continue;
33540                 }
33541                 default: {
33542                   var errMsg = 'Token with "' + token.type + '" type was not found.';
33543                   if (this.options.silent) {
33544                     console.error(errMsg);
33545                     return;
33546                   } else {
33547                     throw new Error(errMsg);
33548                   }
33549                 }
33550               }
33551             }
33552
33553             return out;
33554           };
33555
33556           /**
33557            * Parse Inline Tokens
33558            */
33559           Parser.prototype.parseInline = function parseInline (tokens, renderer) {
33560             renderer = renderer || this.renderer;
33561             var out = '',
33562               i,
33563               token;
33564
33565             var l = tokens.length;
33566             for (i = 0; i < l; i++) {
33567               token = tokens[i];
33568               switch (token.type) {
33569                 case 'escape': {
33570                   out += renderer.text(token.text);
33571                   break;
33572                 }
33573                 case 'html': {
33574                   out += renderer.html(token.text);
33575                   break;
33576                 }
33577                 case 'link': {
33578                   out += renderer.link(token.href, token.title, this.parseInline(token.tokens, renderer));
33579                   break;
33580                 }
33581                 case 'image': {
33582                   out += renderer.image(token.href, token.title, token.text);
33583                   break;
33584                 }
33585                 case 'strong': {
33586                   out += renderer.strong(this.parseInline(token.tokens, renderer));
33587                   break;
33588                 }
33589                 case 'em': {
33590                   out += renderer.em(this.parseInline(token.tokens, renderer));
33591                   break;
33592                 }
33593                 case 'codespan': {
33594                   out += renderer.codespan(token.text);
33595                   break;
33596                 }
33597                 case 'br': {
33598                   out += renderer.br();
33599                   break;
33600                 }
33601                 case 'del': {
33602                   out += renderer.del(this.parseInline(token.tokens, renderer));
33603                   break;
33604                 }
33605                 case 'text': {
33606                   out += renderer.text(token.text);
33607                   break;
33608                 }
33609                 default: {
33610                   var errMsg = 'Token with "' + token.type + '" type was not found.';
33611                   if (this.options.silent) {
33612                     console.error(errMsg);
33613                     return;
33614                   } else {
33615                     throw new Error(errMsg);
33616                   }
33617                 }
33618               }
33619             }
33620             return out;
33621           };
33622
33623           return Parser;
33624         }());
33625
33626         var merge$3 = helpers.merge;
33627         var checkSanitizeDeprecation$1 = helpers.checkSanitizeDeprecation;
33628         var escape$4 = helpers.escape;
33629         var getDefaults = defaults.getDefaults;
33630         var changeDefaults = defaults.changeDefaults;
33631         var defaults$5 = defaults.defaults;
33632
33633         /**
33634          * Marked
33635          */
33636         function marked(src, opt, callback) {
33637           // throw error in case of non string input
33638           if (typeof src === 'undefined' || src === null) {
33639             throw new Error('marked(): input parameter is undefined or null');
33640           }
33641           if (typeof src !== 'string') {
33642             throw new Error('marked(): input parameter is of type '
33643               + Object.prototype.toString.call(src) + ', string expected');
33644           }
33645
33646           if (callback || typeof opt === 'function') {
33647             if (!callback) {
33648               callback = opt;
33649               opt = null;
33650             }
33651
33652             opt = merge$3({}, marked.defaults, opt || {});
33653             checkSanitizeDeprecation$1(opt);
33654             var highlight = opt.highlight;
33655             var tokens,
33656               pending,
33657               i = 0;
33658
33659             try {
33660               tokens = Lexer_1.lex(src, opt);
33661             } catch (e) {
33662               return callback(e);
33663             }
33664
33665             pending = tokens.length;
33666
33667             var done = function(err) {
33668               if (err) {
33669                 opt.highlight = highlight;
33670                 return callback(err);
33671               }
33672
33673               var out;
33674
33675               try {
33676                 out = Parser_1.parse(tokens, opt);
33677               } catch (e) {
33678                 err = e;
33679               }
33680
33681               opt.highlight = highlight;
33682
33683               return err
33684                 ? callback(err)
33685                 : callback(null, out);
33686             };
33687
33688             if (!highlight || highlight.length < 3) {
33689               return done();
33690             }
33691
33692             delete opt.highlight;
33693
33694             if (!pending) { return done(); }
33695
33696             for (; i < tokens.length; i++) {
33697               (function(token) {
33698                 if (token.type !== 'code') {
33699                   return --pending || done();
33700                 }
33701                 return highlight(token.text, token.lang, function(err, code) {
33702                   if (err) { return done(err); }
33703                   if (code == null || code === token.text) {
33704                     return --pending || done();
33705                   }
33706                   token.text = code;
33707                   token.escaped = true;
33708                   --pending || done();
33709                 });
33710               })(tokens[i]);
33711             }
33712
33713             return;
33714           }
33715           try {
33716             opt = merge$3({}, marked.defaults, opt || {});
33717             checkSanitizeDeprecation$1(opt);
33718             return Parser_1.parse(Lexer_1.lex(src, opt), opt);
33719           } catch (e$1) {
33720             e$1.message += '\nPlease report this to https://github.com/markedjs/marked.';
33721             if ((opt || marked.defaults).silent) {
33722               return '<p>An error occurred:</p><pre>'
33723                 + escape$4(e$1.message + '', true)
33724                 + '</pre>';
33725             }
33726             throw e$1;
33727           }
33728         }
33729
33730         /**
33731          * Options
33732          */
33733
33734         marked.options =
33735         marked.setOptions = function(opt) {
33736           merge$3(marked.defaults, opt);
33737           changeDefaults(marked.defaults);
33738           return marked;
33739         };
33740
33741         marked.getDefaults = getDefaults;
33742
33743         marked.defaults = defaults$5;
33744
33745         /**
33746          * Use Extension
33747          */
33748
33749         marked.use = function(extension) {
33750           var opts = merge$3({}, extension);
33751           if (extension.renderer) {
33752             var renderer = marked.defaults.renderer || new Renderer_1();
33753             var loop = function ( prop ) {
33754               var prevRenderer = renderer[prop];
33755               renderer[prop] = function () {
33756                 var args = [], len = arguments.length;
33757                 while ( len-- ) args[ len ] = arguments[ len ];
33758
33759                 var ret = extension.renderer[prop].apply(renderer, args);
33760                 if (ret === false) {
33761                   ret = prevRenderer.apply(renderer, args);
33762                 }
33763                 return ret;
33764               };
33765             };
33766
33767             for (var prop in extension.renderer) loop( prop );
33768             opts.renderer = renderer;
33769           }
33770           if (extension.tokenizer) {
33771             var tokenizer = marked.defaults.tokenizer || new Tokenizer_1();
33772             var loop$1 = function ( prop ) {
33773               var prevTokenizer = tokenizer[prop$1];
33774               tokenizer[prop$1] = function () {
33775                 var args = [], len = arguments.length;
33776                 while ( len-- ) args[ len ] = arguments[ len ];
33777
33778                 var ret = extension.tokenizer[prop$1].apply(tokenizer, args);
33779                 if (ret === false) {
33780                   ret = prevTokenizer.apply(tokenizer, args);
33781                 }
33782                 return ret;
33783               };
33784             };
33785
33786             for (var prop$1 in extension.tokenizer) loop$1( prop );
33787             opts.tokenizer = tokenizer;
33788           }
33789           marked.setOptions(opts);
33790         };
33791
33792         /**
33793          * Expose
33794          */
33795
33796         marked.Parser = Parser_1;
33797         marked.parser = Parser_1.parse;
33798
33799         marked.Renderer = Renderer_1;
33800         marked.TextRenderer = TextRenderer_1;
33801
33802         marked.Lexer = Lexer_1;
33803         marked.lexer = Lexer_1.lex;
33804
33805         marked.Tokenizer = Tokenizer_1;
33806
33807         marked.Slugger = Slugger_1;
33808
33809         marked.parse = marked;
33810
33811         var marked_1 = marked;
33812
33813         var tiler$2 = utilTiler();
33814         var dispatch$3 = dispatch('loaded');
33815         var _tileZoom$2 = 14;
33816         var _osmoseUrlRoot = 'https://osmose.openstreetmap.fr/api/0.3';
33817         var _osmoseData = { icons: {}, items: [] };
33818
33819         // This gets reassigned if reset
33820         var _cache$2;
33821
33822         function abortRequest$2(controller) {
33823           if (controller) {
33824             controller.abort();
33825           }
33826         }
33827
33828         function abortUnwantedRequests$2(cache, tiles) {
33829           Object.keys(cache.inflightTile).forEach(function (k) {
33830             var wanted = tiles.find(function (tile) { return k === tile.id; });
33831             if (!wanted) {
33832               abortRequest$2(cache.inflightTile[k]);
33833               delete cache.inflightTile[k];
33834             }
33835           });
33836         }
33837
33838         function encodeIssueRtree$2(d) {
33839           return { minX: d.loc[0], minY: d.loc[1], maxX: d.loc[0], maxY: d.loc[1], data: d };
33840         }
33841
33842         // Replace or remove QAItem from rtree
33843         function updateRtree$2(item, replace) {
33844           _cache$2.rtree.remove(item, function (a, b) { return a.data.id === b.data.id; });
33845
33846           if (replace) {
33847             _cache$2.rtree.insert(item);
33848           }
33849         }
33850
33851         // Issues shouldn't obscure each other
33852         function preventCoincident$1(loc) {
33853           var coincident = false;
33854           do {
33855             // first time, move marker up. after that, move marker right.
33856             var delta = coincident ? [0.00001, 0] : [0, 0.00001];
33857             loc = geoVecAdd(loc, delta);
33858             var bbox = geoExtent(loc).bbox();
33859             coincident = _cache$2.rtree.search(bbox).length;
33860           } while (coincident);
33861
33862           return loc;
33863         }
33864
33865         var serviceOsmose = {
33866           title: 'osmose',
33867
33868           init: function init() {
33869             _mainFileFetcher.get('qa_data')
33870               .then(function (d) {
33871                 _osmoseData = d.osmose;
33872                 _osmoseData.items = Object.keys(d.osmose.icons)
33873                   .map(function (s) { return s.split('-')[0]; })
33874                   .reduce(function (unique, item) { return unique.indexOf(item) !== -1 ? unique : unique.concat( [item]); }, []);
33875               });
33876
33877             if (!_cache$2) {
33878               this.reset();
33879             }
33880
33881             this.event = utilRebind(this, dispatch$3, 'on');
33882           },
33883
33884           reset: function reset() {
33885             var _strings = {};
33886             var _colors = {};
33887             if (_cache$2) {
33888               Object.values(_cache$2.inflightTile).forEach(abortRequest$2);
33889               // Strings and colors are static and should not be re-populated
33890               _strings = _cache$2.strings;
33891               _colors = _cache$2.colors;
33892             }
33893             _cache$2 = {
33894               data: {},
33895               loadedTile: {},
33896               inflightTile: {},
33897               inflightPost: {},
33898               closed: {},
33899               rtree: new RBush(),
33900               strings: _strings,
33901               colors: _colors
33902             };
33903           },
33904
33905           loadIssues: function loadIssues(projection) {
33906             var this$1 = this;
33907
33908             var params = {
33909               // Tiles return a maximum # of issues
33910               // So we want to filter our request for only types iD supports
33911               item: _osmoseData.items
33912             };
33913
33914             // determine the needed tiles to cover the view
33915             var tiles = tiler$2
33916               .zoomExtent([_tileZoom$2, _tileZoom$2])
33917               .getTiles(projection);
33918
33919             // abort inflight requests that are no longer needed
33920             abortUnwantedRequests$2(_cache$2, tiles);
33921
33922             // issue new requests..
33923             tiles.forEach(function (tile) {
33924               if (_cache$2.loadedTile[tile.id] || _cache$2.inflightTile[tile.id]) { return; }
33925
33926               var ref = tile.xyz;
33927               var x = ref[0];
33928               var y = ref[1];
33929               var z = ref[2];
33930               var url = _osmoseUrlRoot + "/issues/" + z + "/" + x + "/" + y + ".json?" + utilQsString(params);
33931
33932               var controller = new AbortController();
33933               _cache$2.inflightTile[tile.id] = controller;
33934
33935               d3_json(url, { signal: controller.signal })
33936                 .then(function (data) {
33937                   delete _cache$2.inflightTile[tile.id];
33938                   _cache$2.loadedTile[tile.id] = true;
33939
33940                   if (data.features) {
33941                     data.features.forEach(function (issue) {
33942                       var ref = issue.properties;
33943                       var item = ref.item;
33944                       var cl = ref.class;
33945                       var id = ref.uuid;
33946                       /* Osmose issues are uniquely identified by a unique
33947                         `item` and `class` combination (both integer values) */
33948                       var itemType = item + "-" + cl;
33949
33950                       // Filter out unsupported issue types (some are too specific or advanced)
33951                       if (itemType in _osmoseData.icons) {
33952                         var loc = issue.geometry.coordinates; // lon, lat
33953                         loc = preventCoincident$1(loc);
33954
33955                         var d = new QAItem(loc, this$1, itemType, id, { item: item });
33956
33957                         // Setting elems here prevents UI detail requests
33958                         if (item === 8300 || item === 8360) {
33959                           d.elems = [];
33960                         }
33961
33962                         _cache$2.data[d.id] = d;
33963                         _cache$2.rtree.insert(encodeIssueRtree$2(d));
33964                       }
33965                     });
33966                   }
33967
33968                   dispatch$3.call('loaded');
33969                 })
33970                 .catch(function () {
33971                   delete _cache$2.inflightTile[tile.id];
33972                   _cache$2.loadedTile[tile.id] = true;
33973                 });
33974             });
33975           },
33976
33977           loadIssueDetail: function loadIssueDetail(issue) {
33978             var this$1 = this;
33979
33980             // Issue details only need to be fetched once
33981             if (issue.elems !== undefined) {
33982               return Promise.resolve(issue);
33983             }
33984
33985             var url = _osmoseUrlRoot + "/issue/" + (issue.id) + "?langs=" + (_mainLocalizer.localeCode());
33986             var cacheDetails = function (data) {
33987               // Associated elements used for highlighting
33988               // Assign directly for immediate use in the callback
33989               issue.elems = data.elems.map(function (e) { return e.type.substring(0,1) + e.id; });
33990
33991               // Some issues have instance specific detail in a subtitle
33992               issue.detail = data.subtitle ? marked_1(data.subtitle.auto) : '';
33993
33994               this$1.replaceItem(issue);
33995             };
33996
33997             return d3_json(url).then(cacheDetails).then(function () { return issue; });
33998           },
33999
34000           loadStrings: function loadStrings(locale) {
34001             if ( locale === void 0 ) locale=_mainLocalizer.localeCode();
34002
34003             var items = Object.keys(_osmoseData.icons);
34004
34005             if (
34006               locale in _cache$2.strings
34007               && Object.keys(_cache$2.strings[locale]).length === items.length
34008             ) {
34009                 return Promise.resolve(_cache$2.strings[locale]);
34010             }
34011
34012             // May be partially populated already if some requests were successful
34013             if (!(locale in _cache$2.strings)) {
34014               _cache$2.strings[locale] = {};
34015             }
34016
34017             // Only need to cache strings for supported issue types
34018             // Using multiple individual item + class requests to reduce fetched data size
34019             var allRequests = items.map(function (itemType) {
34020               // No need to request data we already have
34021               if (itemType in _cache$2.strings[locale]) { return; }
34022
34023               var cacheData = function (data) {
34024                 // Bunch of nested single value arrays of objects
34025                 var ref = data.categories;
34026                 var cat = ref[0]; if ( cat === void 0 ) cat = {items:[]};
34027                 var ref$1 = cat.items;
34028                 var item = ref$1[0]; if ( item === void 0 ) item = {class:[]};
34029                 var ref$2 = item.class;
34030                 var cl = ref$2[0]; if ( cl === void 0 ) cl = null;
34031
34032                 // If null default value is reached, data wasn't as expected (or was empty)
34033                 if (!cl) {
34034                   /* eslint-disable no-console */
34035                   console.log(("Osmose strings request (" + itemType + ") had unexpected data"));
34036                   /* eslint-enable no-console */
34037                   return;
34038                 }
34039
34040                 // Cache served item colors to automatically style issue markers later
34041                 var itemInt = item.item;
34042                 var color = item.color;
34043                 if (/^#[A-Fa-f0-9]{6}|[A-Fa-f0-9]{3}/.test(color)) {
34044                   _cache$2.colors[itemInt] = color;
34045                 }
34046
34047                 // Value of root key will be null if no string exists
34048                 // If string exists, value is an object with key 'auto' for string
34049                 var title = cl.title;
34050                 var detail = cl.detail;
34051                 var fix = cl.fix;
34052                 var trap = cl.trap;
34053
34054                 // Osmose titles shouldn't contain markdown
34055                 var issueStrings = {};
34056                 if (title) { issueStrings.title = title.auto; }
34057                 if (detail) { issueStrings.detail = marked_1(detail.auto); }
34058                 if (trap) { issueStrings.trap = marked_1(trap.auto); }
34059                 if (fix) { issueStrings.fix = marked_1(fix.auto); }
34060
34061                 _cache$2.strings[locale][itemType] = issueStrings;
34062               };
34063
34064               var ref = itemType.split('-');
34065               var item = ref[0];
34066               var cl = ref[1];
34067
34068               // Osmose API falls back to English strings where untranslated or if locale doesn't exist
34069               var url = _osmoseUrlRoot + "/items/" + item + "/class/" + cl + "?langs=" + locale;
34070
34071               return d3_json(url).then(cacheData);
34072             });
34073
34074             return Promise.all(allRequests).then(function () { return _cache$2.strings[locale]; });
34075           },
34076
34077           getStrings: function getStrings(itemType, locale) {
34078             if ( locale === void 0 ) locale=_mainLocalizer.localeCode();
34079
34080             // No need to fallback to English, Osmose API handles this for us
34081             return (locale in _cache$2.strings) ? _cache$2.strings[locale][itemType] : {};
34082           },
34083
34084           getColor: function getColor(itemType) {
34085             return (itemType in _cache$2.colors) ? _cache$2.colors[itemType] : '#FFFFFF';
34086           },
34087
34088           postUpdate: function postUpdate(issue, callback) {
34089             var this$1 = this;
34090
34091             if (_cache$2.inflightPost[issue.id]) {
34092               return callback({ message: 'Issue update already inflight', status: -2 }, issue);
34093             }
34094
34095             // UI sets the status to either 'done' or 'false'
34096             var url = _osmoseUrlRoot + "/issue/" + (issue.id) + "/" + (issue.newStatus);
34097             var controller = new AbortController();
34098             var after = function () {
34099               delete _cache$2.inflightPost[issue.id];
34100
34101               this$1.removeItem(issue);
34102               if (issue.newStatus === 'done') {
34103                 // Keep track of the number of issues closed per `item` to tag the changeset
34104                 if (!(issue.item in _cache$2.closed)) {
34105                   _cache$2.closed[issue.item] = 0;
34106                 }
34107                 _cache$2.closed[issue.item] += 1;
34108               }
34109               if (callback) { callback(null, issue); }
34110             };
34111
34112             _cache$2.inflightPost[issue.id] = controller;
34113
34114             fetch(url, { signal: controller.signal })
34115               .then(after)
34116               .catch(function (err) {
34117                 delete _cache$2.inflightPost[issue.id];
34118                 if (callback) { callback(err.message); }
34119               });
34120           },
34121
34122           // Get all cached QAItems covering the viewport
34123           getItems: function getItems(projection) {
34124             var viewport = projection.clipExtent();
34125             var min = [viewport[0][0], viewport[1][1]];
34126             var max = [viewport[1][0], viewport[0][1]];
34127             var bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox();
34128
34129             return _cache$2.rtree.search(bbox).map(function (d) { return d.data; });
34130           },
34131
34132           // Get a QAItem from cache
34133           // NOTE: Don't change method name until UI v3 is merged
34134           getError: function getError(id) {
34135             return _cache$2.data[id];
34136           },
34137
34138           // get the name of the icon to display for this item
34139           getIcon: function getIcon(itemType) {
34140             return _osmoseData.icons[itemType];
34141           },
34142
34143           // Replace a single QAItem in the cache
34144           replaceItem: function replaceItem(item) {
34145             if (!(item instanceof QAItem) || !item.id) { return; }
34146
34147             _cache$2.data[item.id] = item;
34148             updateRtree$2(encodeIssueRtree$2(item), true); // true = replace
34149             return item;
34150           },
34151
34152           // Remove a single QAItem from the cache
34153           removeItem: function removeItem(item) {
34154             if (!(item instanceof QAItem) || !item.id) { return; }
34155
34156             delete _cache$2.data[item.id];
34157             updateRtree$2(encodeIssueRtree$2(item), false); // false = remove
34158           },
34159
34160           // Used to populate `closed:osmose:*` changeset tags
34161           getClosedCounts: function getClosedCounts() {
34162             return _cache$2.closed;
34163           },
34164
34165           itemURL: function itemURL(item) {
34166             return ("https://osmose.openstreetmap.fr/en/error/" + (item.id));
34167           }
34168         };
34169
34170         /*
34171             A standalone SVG element that contains only a `defs` sub-element. To be
34172             used once globally, since defs IDs must be unique within a document.
34173         */
34174         function svgDefs(context) {
34175
34176             function drawDefs(selection) {
34177                 var defs = selection.append('defs');
34178
34179                 // add markers
34180                 defs
34181                     .append('marker')
34182                     .attr('id', 'ideditor-oneway-marker')
34183                     .attr('viewBox', '0 0 10 5')
34184                     .attr('refX', 2.5)
34185                     .attr('refY', 2.5)
34186                     .attr('markerWidth', 2)
34187                     .attr('markerHeight', 2)
34188                     .attr('markerUnits', 'strokeWidth')
34189                     .attr('orient', 'auto')
34190                     .append('path')
34191                     .attr('class', 'oneway-marker-path')
34192                     .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')
34193                     .attr('stroke', 'none')
34194                     .attr('fill', '#000')
34195                     .attr('opacity', '0.75');
34196
34197                 // SVG markers have to be given a colour where they're defined
34198                 // (they can't inherit it from the line they're attached to),
34199                 // so we need to manually define markers for each color of tag
34200                 // (also, it's slightly nicer if we can control the
34201                 // positioning for different tags)
34202                 function addSidedMarker(name, color, offset) {
34203                     defs
34204                         .append('marker')
34205                         .attr('id', 'ideditor-sided-marker-' + name)
34206                         .attr('viewBox', '0 0 2 2')
34207                         .attr('refX', 1)
34208                         .attr('refY', -offset)
34209                         .attr('markerWidth', 1.5)
34210                         .attr('markerHeight', 1.5)
34211                         .attr('markerUnits', 'strokeWidth')
34212                         .attr('orient', 'auto')
34213                         .append('path')
34214                         .attr('class', 'sided-marker-path sided-marker-' + name + '-path')
34215                         .attr('d', 'M 0,0 L 1,1 L 2,0 z')
34216                         .attr('stroke', 'none')
34217                         .attr('fill', color);
34218                 }
34219                 addSidedMarker('natural', 'rgb(170, 170, 170)', 0);
34220                 // for a coastline, the arrows are (somewhat unintuitively) on
34221                 // the water side, so let's color them blue (with a gap) to
34222                 // give a stronger indication
34223                 addSidedMarker('coastline', '#77dede', 1);
34224                 addSidedMarker('waterway', '#77dede', 1);
34225                 // barriers have a dashed line, and separating the triangle
34226                 // from the line visually suits that
34227                 addSidedMarker('barrier', '#ddd', 1);
34228                 addSidedMarker('man_made', '#fff', 0);
34229
34230                 defs
34231                     .append('marker')
34232                     .attr('id', 'ideditor-viewfield-marker')
34233                     .attr('viewBox', '0 0 16 16')
34234                     .attr('refX', 8)
34235                     .attr('refY', 16)
34236                     .attr('markerWidth', 4)
34237                     .attr('markerHeight', 4)
34238                     .attr('markerUnits', 'strokeWidth')
34239                     .attr('orient', 'auto')
34240                     .append('path')
34241                     .attr('class', 'viewfield-marker-path')
34242                     .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')
34243                     .attr('fill', '#333')
34244                     .attr('fill-opacity', '0.75')
34245                     .attr('stroke', '#fff')
34246                     .attr('stroke-width', '0.5px')
34247                     .attr('stroke-opacity', '0.75');
34248
34249                 defs
34250                     .append('marker')
34251                     .attr('id', 'ideditor-viewfield-marker-wireframe')
34252                     .attr('viewBox', '0 0 16 16')
34253                     .attr('refX', 8)
34254                     .attr('refY', 16)
34255                     .attr('markerWidth', 4)
34256                     .attr('markerHeight', 4)
34257                     .attr('markerUnits', 'strokeWidth')
34258                     .attr('orient', 'auto')
34259                     .append('path')
34260                     .attr('class', 'viewfield-marker-path')
34261                     .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')
34262                     .attr('fill', 'none')
34263                     .attr('stroke', '#fff')
34264                     .attr('stroke-width', '0.5px')
34265                     .attr('stroke-opacity', '0.75');
34266
34267                 // add patterns
34268                 var patterns = defs.selectAll('pattern')
34269                     .data([
34270                         // pattern name, pattern image name
34271                         ['beach', 'dots'],
34272                         ['construction', 'construction'],
34273                         ['cemetery', 'cemetery'],
34274                         ['cemetery_christian', 'cemetery_christian'],
34275                         ['cemetery_buddhist', 'cemetery_buddhist'],
34276                         ['cemetery_muslim', 'cemetery_muslim'],
34277                         ['cemetery_jewish', 'cemetery_jewish'],
34278                         ['farmland', 'farmland'],
34279                         ['farmyard', 'farmyard'],
34280                         ['forest', 'forest'],
34281                         ['forest_broadleaved', 'forest_broadleaved'],
34282                         ['forest_needleleaved', 'forest_needleleaved'],
34283                         ['forest_leafless', 'forest_leafless'],
34284                         ['golf_green', 'grass'],
34285                         ['grass', 'grass'],
34286                         ['landfill', 'landfill'],
34287                         ['meadow', 'grass'],
34288                         ['orchard', 'orchard'],
34289                         ['pond', 'pond'],
34290                         ['quarry', 'quarry'],
34291                         ['scrub', 'bushes'],
34292                         ['vineyard', 'vineyard'],
34293                         ['water_standing', 'lines'],
34294                         ['waves', 'waves'],
34295                         ['wetland', 'wetland'],
34296                         ['wetland_marsh', 'wetland_marsh'],
34297                         ['wetland_swamp', 'wetland_swamp'],
34298                         ['wetland_bog', 'wetland_bog'],
34299                         ['wetland_reedbed', 'wetland_reedbed']
34300                     ])
34301                     .enter()
34302                     .append('pattern')
34303                     .attr('id', function (d) { return 'ideditor-pattern-' + d[0]; })
34304                     .attr('width', 32)
34305                     .attr('height', 32)
34306                     .attr('patternUnits', 'userSpaceOnUse');
34307
34308                 patterns
34309                     .append('rect')
34310                     .attr('x', 0)
34311                     .attr('y', 0)
34312                     .attr('width', 32)
34313                     .attr('height', 32)
34314                     .attr('class', function (d) { return 'pattern-color-' + d[0]; });
34315
34316                 patterns
34317                     .append('image')
34318                     .attr('x', 0)
34319                     .attr('y', 0)
34320                     .attr('width', 32)
34321                     .attr('height', 32)
34322                     .attr('xlink:href', function (d) {
34323                         return context.imagePath('pattern/' + d[1] + '.png');
34324                     });
34325
34326                 // add clip paths
34327                 defs.selectAll('clipPath')
34328                     .data([12, 18, 20, 32, 45])
34329                     .enter()
34330                     .append('clipPath')
34331                     .attr('id', function (d) { return 'ideditor-clip-square-' + d; })
34332                     .append('rect')
34333                     .attr('x', 0)
34334                     .attr('y', 0)
34335                     .attr('width', function (d) { return d; })
34336                     .attr('height', function (d) { return d; });
34337
34338                 // add symbol spritesheets
34339                 defs
34340                     .call(drawDefs.addSprites, [
34341                         'iD-sprite', 'maki-sprite', 'temaki-sprite', 'fa-sprite', 'tnp-sprite', 'community-sprite'
34342                     ], true);
34343             }
34344
34345
34346             drawDefs.addSprites = function(selection, ids, overrideColors) {
34347                 var spritesheets = selection.selectAll('.spritesheet');
34348                 var currData = spritesheets.data();
34349                 var data = utilArrayUniq(currData.concat(ids));
34350
34351                 spritesheets
34352                     .data(data)
34353                     .enter()
34354                     .append('g')
34355                     .attr('class', function(d) { return 'spritesheet spritesheet-' + d; })
34356                     .each(function(d) {
34357                         var url = context.imagePath(d + '.svg');
34358                         var node = select(this).node();
34359
34360                         svg(url)
34361                             .then(function(svg) {
34362                                 node.appendChild(
34363                                     select(svg.documentElement).attr('id', 'ideditor-' + d).node()
34364                                 );
34365                                 if (overrideColors && d !== 'iD-sprite') {   // allow icon colors to be overridden..
34366                                     select(node).selectAll('path')
34367                                         .attr('fill', 'currentColor');
34368                                 }
34369                             })
34370                             .catch(function() {
34371                                 /* ignore */
34372                             });
34373                     });
34374             };
34375
34376
34377             return drawDefs;
34378         }
34379
34380         /* global Mapillary:false */
34381
34382
34383         var apibase = 'https://a.mapillary.com/v3/';
34384         var viewercss = 'mapillary-js/mapillary.min.css';
34385         var viewerjs = 'mapillary-js/mapillary.min.js';
34386         var clientId = 'NzNRM2otQkR2SHJzaXJmNmdQWVQ0dzo1ZWYyMmYwNjdmNDdlNmVi';
34387         var mapFeatureConfig = {
34388             values: [
34389                 'construction--flat--crosswalk-plain',
34390                 'marking--discrete--crosswalk-zebra',
34391                 'object--banner',
34392                 'object--bench',
34393                 'object--bike-rack',
34394                 'object--billboard',
34395                 'object--catch-basin',
34396                 'object--cctv-camera',
34397                 'object--fire-hydrant',
34398                 'object--mailbox',
34399                 'object--manhole',
34400                 'object--phone-booth',
34401                 'object--sign--advertisement',
34402                 'object--sign--information',
34403                 'object--sign--store',
34404                 'object--street-light',
34405                 'object--support--utility-pole',
34406                 'object--traffic-light--*',
34407                 'object--traffic-light--pedestrians',
34408                 'object--trash-can'
34409             ].join(',')
34410         };
34411         var maxResults = 1000;
34412         var tileZoom = 14;
34413         var tiler$3 = utilTiler().zoomExtent([tileZoom, tileZoom]).skipNullIsland(true);
34414         var dispatch$4 = dispatch('loadedImages', 'loadedSigns', 'loadedMapFeatures', 'bearingChanged');
34415         var _mlyFallback = false;
34416         var _mlyCache;
34417         var _mlyClicks;
34418         var _mlySelectedImageKey;
34419         var _mlyViewer;
34420
34421
34422         function abortRequest$3(controller) {
34423             controller.abort();
34424         }
34425
34426
34427         function maxPageAtZoom(z) {
34428             if (z < 15)   { return 2; }
34429             if (z === 15) { return 5; }
34430             if (z === 16) { return 10; }
34431             if (z === 17) { return 20; }
34432             if (z === 18) { return 40; }
34433             if (z > 18)   { return 80; }
34434         }
34435
34436
34437         function loadTiles(which, url, projection) {
34438             var currZoom = Math.floor(geoScaleToZoom(projection.scale()));
34439             var tiles = tiler$3.getTiles(projection);
34440
34441             // abort inflight requests that are no longer needed
34442             var cache = _mlyCache[which];
34443             Object.keys(cache.inflight).forEach(function(k) {
34444                 var wanted = tiles.find(function(tile) { return k.indexOf(tile.id + ',') === 0; });
34445                 if (!wanted) {
34446                     abortRequest$3(cache.inflight[k]);
34447                     delete cache.inflight[k];
34448                 }
34449             });
34450
34451             tiles.forEach(function(tile) {
34452                 loadNextTilePage(which, currZoom, url, tile);
34453             });
34454         }
34455
34456
34457         function loadNextTilePage(which, currZoom, url, tile) {
34458             var cache = _mlyCache[which];
34459             var rect = tile.extent.rectangle();
34460             var maxPages = maxPageAtZoom(currZoom);
34461             var nextPage = cache.nextPage[tile.id] || 0;
34462             var nextURL = cache.nextURL[tile.id] || url +
34463                 utilQsString({
34464                     per_page: maxResults,
34465                     page: nextPage,
34466                     client_id: clientId,
34467                     bbox: [rect[0], rect[1], rect[2], rect[3]].join(','),
34468                 });
34469
34470             if (nextPage > maxPages) { return; }
34471
34472             var id = tile.id + ',' + String(nextPage);
34473             if (cache.loaded[id] || cache.inflight[id]) { return; }
34474
34475             var controller = new AbortController();
34476             cache.inflight[id] = controller;
34477
34478             var options = {
34479                 method: 'GET',
34480                 signal: controller.signal,
34481                 headers: { 'Content-Type': 'application/json' }
34482             };
34483
34484             fetch(nextURL, options)
34485                 .then(function(response) {
34486                     if (!response.ok) {
34487                         throw new Error(response.status + ' ' + response.statusText);
34488                     }
34489                     var linkHeader = response.headers.get('Link');
34490                     if (linkHeader) {
34491                         var pagination = parsePagination(linkHeader);
34492                         if (pagination.next) {
34493                             cache.nextURL[tile.id] = pagination.next;
34494                         }
34495                     }
34496                     return response.json();
34497                 })
34498                 .then(function(data) {
34499                     cache.loaded[id] = true;
34500                     delete cache.inflight[id];
34501                     if (!data || !data.features || !data.features.length) {
34502                         throw new Error('No Data');
34503                     }
34504
34505                     var features = data.features.map(function(feature) {
34506                         var loc = feature.geometry.coordinates;
34507                         var d;
34508
34509                         // An image (shown as a green dot on the map) is a single street photo with extra
34510                         // information such as location, camera angle (CA), camera model, and so on.
34511                         // Each image feature is a GeoJSON Point
34512                         if (which === 'images') {
34513                             d = {
34514                                 loc: loc,
34515                                 key: feature.properties.key,
34516                                 ca: feature.properties.ca,
34517                                 captured_at: feature.properties.captured_at,
34518                                 captured_by: feature.properties.username,
34519                                 pano: feature.properties.pano
34520                             };
34521
34522                             cache.forImageKey[d.key] = d;     // cache imageKey -> image
34523
34524                         // Mapillary organizes images as sequences. A sequence of images are continuously captured
34525                         // by a user at a give time. Sequences are shown on the map as green lines.
34526                         // Each sequence feature is a GeoJSON LineString
34527                         } else if (which === 'sequences') {
34528                             var sequenceKey = feature.properties.key;
34529                             cache.lineString[sequenceKey] = feature;           // cache sequenceKey -> lineString
34530                             feature.properties.coordinateProperties.image_keys.forEach(function(imageKey) {
34531                                 cache.forImageKey[imageKey] = sequenceKey;     // cache imageKey -> sequenceKey
34532                             });
34533                             return false;    // because no `d` data worth loading into an rbush
34534
34535                         // An image detection is a semantic pixel area on an image. The area could indicate
34536                         // sky, trees, sidewalk in the image. A detection can be a polygon, a bounding box, or a point.
34537                         // Each image_detection feature is a GeoJSON Point (located where the image was taken)
34538                         } else if (which === 'image_detections') {
34539                             d = {
34540                                 key: feature.properties.key,
34541                                 image_key: feature.properties.image_key,
34542                                 value: feature.properties.value,
34543                                 package: feature.properties.package,
34544                                 shape: feature.properties.shape
34545                             };
34546
34547                             // cache imageKey -> image_detections
34548                             if (!cache.forImageKey[d.image_key]) {
34549                                 cache.forImageKey[d.image_key] = [];
34550                             }
34551                             cache.forImageKey[d.image_key].push(d);
34552                             return false;    // because no `d` data worth loading into an rbush
34553
34554
34555                         // A map feature is a real world object that can be shown on a map. It could be any object
34556                         // recognized from images, manually added in images, or added on the map.
34557                         // Each map feature is a GeoJSON Point (located where the feature is)
34558                         } else if (which === 'map_features' || which === 'points') {
34559                             d = {
34560                                 loc: loc,
34561                                 key: feature.properties.key,
34562                                 value: feature.properties.value,
34563                                 package: feature.properties.package,
34564                                 detections: feature.properties.detections
34565                             };
34566                         }
34567
34568                         return {
34569                             minX: loc[0], minY: loc[1], maxX: loc[0], maxY: loc[1], data: d
34570                         };
34571
34572                     }).filter(Boolean);
34573
34574                     if (cache.rtree && features) {
34575                         cache.rtree.load(features);
34576                     }
34577
34578                     if (data.features.length === maxResults) {  // more pages to load
34579                         cache.nextPage[tile.id] = nextPage + 1;
34580                         loadNextTilePage(which, currZoom, url, tile);
34581                     } else {
34582                         cache.nextPage[tile.id] = Infinity;     // no more pages to load
34583                     }
34584
34585                     if (which === 'images' || which === 'sequences') {
34586                         dispatch$4.call('loadedImages');
34587                     } else if (which === 'map_features') {
34588                         dispatch$4.call('loadedSigns');
34589                     } else if (which === 'points') {
34590                         dispatch$4.call('loadedMapFeatures');
34591                     }
34592                 })
34593                 .catch(function() {
34594                     cache.loaded[id] = true;
34595                     delete cache.inflight[id];
34596                 });
34597         }
34598
34599         // extract links to pages of API results
34600         function parsePagination(links) {
34601             return links.split(',').map(function(rel) {
34602                 var elements = rel.split(';');
34603                 if (elements.length === 2) {
34604                     return [
34605                         /<(.+)>/.exec(elements[0])[1],
34606                         /rel="(.+)"/.exec(elements[1])[1]
34607                     ];
34608                 } else {
34609                     return ['',''];
34610                 }
34611             }).reduce(function(pagination, val) {
34612                 pagination[val[1]] = val[0];
34613                 return pagination;
34614             }, {});
34615         }
34616
34617
34618         // partition viewport into higher zoom tiles
34619         function partitionViewport(projection) {
34620             var z = geoScaleToZoom(projection.scale());
34621             var z2 = (Math.ceil(z * 2) / 2) + 2.5;   // round to next 0.5 and add 2.5
34622             var tiler = utilTiler().zoomExtent([z2, z2]);
34623
34624             return tiler.getTiles(projection)
34625                 .map(function(tile) { return tile.extent; });
34626         }
34627
34628
34629         // no more than `limit` results per partition.
34630         function searchLimited(limit, projection, rtree) {
34631             limit = limit || 5;
34632
34633             return partitionViewport(projection)
34634                 .reduce(function(result, extent) {
34635                     var found = rtree.search(extent.bbox())
34636                         .slice(0, limit)
34637                         .map(function(d) { return d.data; });
34638
34639                     return (found.length ? result.concat(found) : result);
34640                 }, []);
34641         }
34642
34643
34644
34645         var serviceMapillary = {
34646
34647             init: function() {
34648                 if (!_mlyCache) {
34649                     this.reset();
34650                 }
34651
34652                 this.event = utilRebind(this, dispatch$4, 'on');
34653             },
34654
34655             reset: function() {
34656                 if (_mlyCache) {
34657                     Object.values(_mlyCache.images.inflight).forEach(abortRequest$3);
34658                     Object.values(_mlyCache.image_detections.inflight).forEach(abortRequest$3);
34659                     Object.values(_mlyCache.map_features.inflight).forEach(abortRequest$3);
34660                     Object.values(_mlyCache.points.inflight).forEach(abortRequest$3);
34661                     Object.values(_mlyCache.sequences.inflight).forEach(abortRequest$3);
34662                 }
34663
34664                 _mlyCache = {
34665                     images: { inflight: {}, loaded: {}, nextPage: {}, nextURL: {}, rtree: new RBush(), forImageKey: {} },
34666                     image_detections: { inflight: {}, loaded: {}, nextPage: {}, nextURL: {}, forImageKey: {} },
34667                     map_features: { inflight: {}, loaded: {}, nextPage: {}, nextURL: {}, rtree: new RBush() },
34668                     points: { inflight: {}, loaded: {}, nextPage: {}, nextURL: {}, rtree: new RBush() },
34669                     sequences: { inflight: {}, loaded: {}, nextPage: {}, nextURL: {}, rtree: new RBush(), forImageKey: {}, lineString: {} }
34670                 };
34671
34672                 _mlySelectedImageKey = null;
34673                 _mlyClicks = [];
34674             },
34675
34676
34677             images: function(projection) {
34678                 var limit = 5;
34679                 return searchLimited(limit, projection, _mlyCache.images.rtree);
34680             },
34681
34682
34683             signs: function(projection) {
34684                 var limit = 5;
34685                 return searchLimited(limit, projection, _mlyCache.map_features.rtree);
34686             },
34687
34688
34689             mapFeatures: function(projection) {
34690                 var limit = 5;
34691                 return searchLimited(limit, projection, _mlyCache.points.rtree);
34692             },
34693
34694
34695             cachedImage: function(imageKey) {
34696                 return _mlyCache.images.forImageKey[imageKey];
34697             },
34698
34699
34700             sequences: function(projection) {
34701                 var viewport = projection.clipExtent();
34702                 var min = [viewport[0][0], viewport[1][1]];
34703                 var max = [viewport[1][0], viewport[0][1]];
34704                 var bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox();
34705                 var sequenceKeys = {};
34706
34707                 // all sequences for images in viewport
34708                 _mlyCache.images.rtree.search(bbox)
34709                     .forEach(function(d) {
34710                         var sequenceKey = _mlyCache.sequences.forImageKey[d.data.key];
34711                         if (sequenceKey) {
34712                             sequenceKeys[sequenceKey] = true;
34713                         }
34714                     });
34715
34716                 // Return lineStrings for the sequences
34717                 return Object.keys(sequenceKeys).map(function(sequenceKey) {
34718                     return _mlyCache.sequences.lineString[sequenceKey];
34719                 });
34720             },
34721
34722
34723             signsSupported: function() {
34724                 return true;
34725             },
34726
34727
34728             loadImages: function(projection) {
34729                 loadTiles('images', apibase + 'images?sort_by=key&', projection);
34730                 loadTiles('sequences', apibase + 'sequences?sort_by=key&', projection);
34731             },
34732
34733
34734             loadSigns: function(projection) {
34735                 // if we are looking at signs, we'll actually need to fetch images too
34736                 loadTiles('images', apibase + 'images?sort_by=key&', projection);
34737                 loadTiles('map_features', apibase + 'map_features?layers=trafficsigns&min_nbr_image_detections=2&sort_by=key&', projection);
34738                 loadTiles('image_detections', apibase + 'image_detections?layers=trafficsigns&sort_by=key&', projection);
34739             },
34740
34741
34742             loadMapFeatures: function(projection) {
34743                 // if we are looking at signs, we'll actually need to fetch images too
34744                 loadTiles('images', apibase + 'images?sort_by=key', projection);
34745                 loadTiles('points', apibase + 'map_features?layers=points&min_nbr_image_detections=2&sort_by=key&values=' + mapFeatureConfig.values + '&', projection);
34746                 loadTiles('image_detections', apibase + 'image_detections?layers=points&sort_by=key&values=' + mapFeatureConfig.values + '&', projection);
34747             },
34748
34749
34750             loadViewer: function(context) {
34751                 // add mly-wrapper
34752                 var wrap = context.container().select('.photoviewer')
34753                     .selectAll('.mly-wrapper')
34754                     .data([0]);
34755
34756                 wrap.enter()
34757                     .append('div')
34758                     .attr('id', 'ideditor-mly')
34759                     .attr('class', 'photo-wrapper mly-wrapper')
34760                     .classed('hide', true);
34761
34762                 // load mapillary-viewercss
34763                 select('head').selectAll('#ideditor-mapillary-viewercss')
34764                     .data([0])
34765                     .enter()
34766                     .append('link')
34767                     .attr('id', 'ideditor-mapillary-viewercss')
34768                     .attr('rel', 'stylesheet')
34769                     .attr('href', context.asset(viewercss));
34770
34771                 // load mapillary-viewerjs
34772                 select('head').selectAll('#ideditor-mapillary-viewerjs')
34773                     .data([0])
34774                     .enter()
34775                     .append('script')
34776                     .attr('id', 'ideditor-mapillary-viewerjs')
34777                     .attr('src', context.asset(viewerjs));
34778
34779                 // load mapillary signs sprite
34780                 var defs = context.container().select('defs');
34781                 defs.call(svgDefs(context).addSprites, ['mapillary-sprite', 'mapillary-object-sprite'], false /* don't override colors */ );
34782
34783                 // Register viewer resize handler
34784                 context.ui().photoviewer.on('resize.mapillary', function() {
34785                     if (_mlyViewer) {
34786                         _mlyViewer.resize();
34787                     }
34788                 });
34789             },
34790
34791
34792             showViewer: function(context) {
34793                 var wrap = context.container().select('.photoviewer')
34794                     .classed('hide', false);
34795
34796                 var isHidden = wrap.selectAll('.photo-wrapper.mly-wrapper.hide').size();
34797
34798                 if (isHidden && _mlyViewer) {
34799                     wrap
34800                         .selectAll('.photo-wrapper:not(.mly-wrapper)')
34801                         .classed('hide', true);
34802
34803                     wrap
34804                         .selectAll('.photo-wrapper.mly-wrapper')
34805                         .classed('hide', false);
34806
34807                     _mlyViewer.resize();
34808                 }
34809
34810                 return this;
34811             },
34812
34813
34814             hideViewer: function(context) {
34815                 _mlySelectedImageKey = null;
34816
34817                 if (!_mlyFallback && _mlyViewer) {
34818                     _mlyViewer.getComponent('sequence').stop();
34819                 }
34820
34821                 var viewer = context.container().select('.photoviewer');
34822                 if (!viewer.empty()) { viewer.datum(null); }
34823
34824                 viewer
34825                     .classed('hide', true)
34826                     .selectAll('.photo-wrapper')
34827                     .classed('hide', true);
34828
34829                 context.container().selectAll('.viewfield-group, .sequence, .icon-detected')
34830                     .classed('currentView', false);
34831
34832                 return this.setStyles(context, null, true);
34833             },
34834
34835
34836             parsePagination: parsePagination,
34837
34838
34839             updateViewer: function(context, imageKey) {
34840                 if (!imageKey) { return this; }
34841
34842                 if (!_mlyViewer) {
34843                     this.initViewer(context, imageKey);
34844                 } else {
34845                     _mlyViewer.moveToKey(imageKey)
34846                         .catch(function(e) { console.error('mly3', e); });  // eslint-disable-line no-console
34847                 }
34848
34849                 return this;
34850             },
34851
34852
34853             initViewer: function(context, imageKey) {
34854                 var that = this;
34855                 if (window.Mapillary && imageKey) {
34856                     var opts = {
34857                         baseImageSize: 320,
34858                         component: {
34859                             cover: false,
34860                             keyboard: false,
34861                             tag: true
34862                         }
34863                     };
34864
34865                     // Disable components requiring WebGL support
34866                     if (!Mapillary.isSupported() && Mapillary.isFallbackSupported()) {
34867                         _mlyFallback = true;
34868                         opts.component = {
34869                             cover: false,
34870                             direction: false,
34871                             imagePlane: false,
34872                             keyboard: false,
34873                             mouse: false,
34874                             sequence: false,
34875                             tag: false,
34876                             image: true,        // fallback
34877                             navigation: true    // fallback
34878                         };
34879                     }
34880
34881                     _mlyViewer = new Mapillary.Viewer('ideditor-mly', clientId, null, opts);
34882                     _mlyViewer.on('nodechanged', nodeChanged);
34883                     _mlyViewer.on('bearingchanged', bearingChanged);
34884                     _mlyViewer.moveToKey(imageKey)
34885                         .catch(function(e) { console.error('mly3', e); });  // eslint-disable-line no-console
34886                 }
34887
34888                 // nodeChanged: called after the viewer has changed images and is ready.
34889                 //
34890                 // There is some logic here to batch up clicks into a _mlyClicks array
34891                 // because the user might click on a lot of markers quickly and nodechanged
34892                 // may be called out of order asynchronously.
34893                 //
34894                 // Clicks are added to the array in `selectedImage` and removed here.
34895                 //
34896                 function nodeChanged(node) {
34897                     if (!_mlyFallback) {
34898                         _mlyViewer.getComponent('tag').removeAll();  // remove previous detections
34899                     }
34900
34901                     var clicks = _mlyClicks;
34902                     var index = clicks.indexOf(node.key);
34903                     var selectedKey = _mlySelectedImageKey;
34904
34905                     if (index > -1) {              // `nodechanged` initiated from clicking on a marker..
34906                         clicks.splice(index, 1);   // remove the click
34907                         // If `node.key` matches the current _mlySelectedImageKey, call `selectImage()`
34908                         // one more time to update the detections and attribution..
34909                         if (node.key === selectedKey) {
34910                             that.selectImage(context, _mlySelectedImageKey, true);
34911                         }
34912                     } else {             // `nodechanged` initiated from the Mapillary viewer controls..
34913                         var loc = node.computedLatLon ? [node.computedLatLon.lon, node.computedLatLon.lat] : [node.latLon.lon, node.latLon.lat];
34914                         context.map().centerEase(loc);
34915                         that.selectImage(context, node.key, true);
34916                     }
34917                 }
34918
34919                 function bearingChanged(e) {
34920                     dispatch$4.call('bearingChanged', undefined, e);
34921                 }
34922             },
34923
34924
34925             // Pass in the image key string as `imageKey`.
34926             // This allows images to be selected from places that dont have access
34927             // to the full image datum (like the street signs layer or the js viewer)
34928             selectImage: function(context, imageKey, fromViewer) {
34929
34930                 _mlySelectedImageKey = imageKey;
34931
34932                 // Note the datum could be missing, but we'll try to carry on anyway.
34933                 // There just might be a delay before user sees detections, captured_at, etc.
34934                 var d = _mlyCache.images.forImageKey[imageKey];
34935
34936                 var viewer = context.container().select('.photoviewer');
34937                 if (!viewer.empty()) { viewer.datum(d); }
34938
34939                 imageKey = (d && d.key) || imageKey;
34940                 if (!fromViewer && imageKey) {
34941                     _mlyClicks.push(imageKey);
34942                 }
34943
34944                 this.setStyles(context, null, true);
34945
34946                 // if signs signs are shown, highlight the ones that appear in this image
34947                 context.container().selectAll('.layer-mapillary-signs .icon-detected')
34948                     .classed('currentView', function(d) {
34949                         return d.detections.some(function(detection) {
34950                             return detection.image_key === imageKey;
34951                         });
34952                     });
34953
34954                 if (d) {
34955                     this.updateDetections(d);
34956                 }
34957
34958                 return this;
34959             },
34960
34961
34962             getSelectedImageKey: function() {
34963                 return _mlySelectedImageKey;
34964             },
34965
34966
34967             getSequenceKeyForImageKey: function(imageKey) {
34968                 return _mlyCache.sequences.forImageKey[imageKey];
34969             },
34970
34971
34972             // Updates the currently highlighted sequence and selected bubble.
34973             // Reset is only necessary when interacting with the viewport because
34974             // this implicitly changes the currently selected bubble/sequence
34975             setStyles: function(context, hovered, reset) {
34976                 if (reset) {  // reset all layers
34977                     context.container().selectAll('.viewfield-group')
34978                         .classed('highlighted', false)
34979                         .classed('hovered', false)
34980                         .classed('currentView', false);
34981
34982                     context.container().selectAll('.sequence')
34983                         .classed('highlighted', false)
34984                         .classed('currentView', false);
34985                 }
34986
34987                 var hoveredImageKey = hovered && hovered.key;
34988                 var hoveredSequenceKey = hoveredImageKey && this.getSequenceKeyForImageKey(hoveredImageKey);
34989                 var hoveredLineString = hoveredSequenceKey && _mlyCache.sequences.lineString[hoveredSequenceKey];
34990                 var hoveredImageKeys = (hoveredLineString && hoveredLineString.properties.coordinateProperties.image_keys) || [];
34991
34992                 var selectedImageKey = _mlySelectedImageKey;
34993                 var selectedSequenceKey = selectedImageKey && this.getSequenceKeyForImageKey(selectedImageKey);
34994                 var selectedLineString = selectedSequenceKey && _mlyCache.sequences.lineString[selectedSequenceKey];
34995                 var selectedImageKeys = (selectedLineString && selectedLineString.properties.coordinateProperties.image_keys) || [];
34996
34997                 // highlight sibling viewfields on either the selected or the hovered sequences
34998                 var highlightedImageKeys = utilArrayUnion(hoveredImageKeys, selectedImageKeys);
34999
35000                 context.container().selectAll('.layer-mapillary .viewfield-group')
35001                     .classed('highlighted', function(d) { return highlightedImageKeys.indexOf(d.key) !== -1; })
35002                     .classed('hovered', function(d) { return d.key === hoveredImageKey; })
35003                     .classed('currentView', function(d) { return d.key === selectedImageKey; });
35004
35005                 context.container().selectAll('.layer-mapillary .sequence')
35006                     .classed('highlighted', function(d) { return d.properties.key === hoveredSequenceKey; })
35007                     .classed('currentView', function(d) { return d.properties.key === selectedSequenceKey; });
35008
35009                 // update viewfields if needed
35010                 context.container().selectAll('.viewfield-group .viewfield')
35011                     .attr('d', viewfieldPath);
35012
35013                 function viewfieldPath() {
35014                     var d = this.parentNode.__data__;
35015                     if (d.pano && d.key !== selectedImageKey) {
35016                         return 'M 8,13 m -10,0 a 10,10 0 1,0 20,0 a 10,10 0 1,0 -20,0';
35017                     } else {
35018                         return 'M 6,9 C 8,8.4 8,8.4 10,9 L 16,-2 C 12,-5 4,-5 0,-2 z';
35019                     }
35020                 }
35021
35022                 return this;
35023             },
35024
35025
35026             updateDetections: function(d) {
35027                 if (!_mlyViewer || _mlyFallback) { return; }
35028
35029                 var imageKey = d && d.key;
35030                 if (!imageKey) { return; }
35031
35032                 var detections = _mlyCache.image_detections.forImageKey[imageKey] || [];
35033                 detections.forEach(function(data) {
35034                     var tag = makeTag(data);
35035                     if (tag) {
35036                         var tagComponent = _mlyViewer.getComponent('tag');
35037                         tagComponent.add([tag]);
35038                     }
35039                 });
35040
35041                 function makeTag(data) {
35042                     var valueParts = data.value.split('--');
35043                     if (valueParts.length !== 3) { return; }
35044
35045                     var text = valueParts[1].replace(/-/g, ' ');
35046                     var tag;
35047
35048                     // Currently only two shapes <Polygon|Point>
35049                     if (data.shape.type === 'Polygon') {
35050                         var polygonGeometry = new Mapillary
35051                             .TagComponent
35052                             .PolygonGeometry(data.shape.coordinates[0]);
35053
35054                         tag = new Mapillary.TagComponent.OutlineTag(
35055                             data.key,
35056                             polygonGeometry,
35057                             {
35058                                 text: text,
35059                                 textColor: 0xffff00,
35060                                 lineColor: 0xffff00,
35061                                 lineWidth: 2,
35062                                 fillColor: 0xffff00,
35063                                 fillOpacity: 0.3,
35064                             }
35065                         );
35066
35067                     } else if (data.shape.type === 'Point') {
35068                         var pointGeometry = new Mapillary
35069                             .TagComponent
35070                             .PointGeometry(data.shape.coordinates[0]);
35071
35072                         tag = new Mapillary.TagComponent.SpotTag(
35073                             data.key,
35074                             pointGeometry,
35075                             {
35076                                 text: text,
35077                                 color: 0xffff00,
35078                                 textColor: 0xffff00
35079                             }
35080                         );
35081                     }
35082
35083                     return tag;
35084                 }
35085             },
35086
35087
35088             cache: function() {
35089                 return _mlyCache;
35090             }
35091
35092         };
35093
35094         function validationIssue(attrs) {
35095             this.type = attrs.type;                // required - name of rule that created the issue (e.g. 'missing_tag')
35096             this.subtype = attrs.subtype;          // optional - category of the issue within the type (e.g. 'relation_type' under 'missing_tag')
35097             this.severity = attrs.severity;        // required - 'warning' or 'error'
35098             this.message = attrs.message;          // required - function returning localized string
35099             this.reference = attrs.reference;      // optional - function(selection) to render reference information
35100             this.entityIds = attrs.entityIds;      // optional - array of IDs of entities involved in the issue
35101             this.loc = attrs.loc;                  // optional - [lon, lat] to zoom in on to see the issue
35102             this.data = attrs.data;                // optional - object containing extra data for the fixes
35103             this.dynamicFixes = attrs.dynamicFixes;// optional - function(context) returning fixes
35104             this.hash = attrs.hash;                // optional - string to further differentiate the issue
35105
35106             this.id = generateID.apply(this);      // generated - see below
35107             this.autoFix = null;                   // generated - if autofix exists, will be set below
35108
35109             // A unique, deterministic string hash.
35110             // Issues with identical id values are considered identical.
35111             function generateID() {
35112                 var parts = [this.type];
35113
35114                 if (this.hash) {   // subclasses can pass in their own differentiator
35115                     parts.push(this.hash);
35116                 }
35117
35118                 if (this.subtype) {
35119                     parts.push(this.subtype);
35120                 }
35121
35122                 // include the entities this issue is for
35123                 // (sort them so the id is deterministic)
35124                 if (this.entityIds) {
35125                     var entityKeys = this.entityIds.slice().sort();
35126                     parts.push.apply(parts, entityKeys);
35127                 }
35128
35129                 return parts.join(':');
35130             }
35131
35132             this.extent = function(resolver) {
35133                 if (this.loc) {
35134                     return geoExtent(this.loc);
35135                 }
35136                 if (this.entityIds && this.entityIds.length) {
35137                     return this.entityIds.reduce(function(extent, entityId) {
35138                         return extent.extend(resolver.entity(entityId).extent(resolver));
35139                     }, geoExtent());
35140                 }
35141                 return null;
35142             };
35143
35144             this.fixes = function(context) {
35145                 var fixes = this.dynamicFixes ? this.dynamicFixes(context) : [];
35146                 var issue = this;
35147
35148                 if (issue.severity === 'warning') {
35149                     // allow ignoring any issue that's not an error
35150                     fixes.push(new validationIssueFix({
35151                         title: _t('issues.fix.ignore_issue.title'),
35152                         icon: 'iD-icon-close',
35153                         onClick: function() {
35154                             context.validator().ignoreIssue(this.issue.id);
35155                         }
35156                     }));
35157                 }
35158
35159                 fixes.forEach(function(fix) {
35160                     fix.id = fix.title;
35161                     // add a reference to the issue for use in actions
35162                     fix.issue = issue;
35163                     if (fix.autoArgs) {
35164                         issue.autoFix = fix;
35165                     }
35166                 });
35167                 return fixes;
35168             };
35169
35170         }
35171
35172
35173         function validationIssueFix(attrs) {
35174             this.title = attrs.title;                   // Required
35175             this.onClick = attrs.onClick;               // Optional - the function to run to apply the fix
35176             this.disabledReason = attrs.disabledReason; // Optional - a string explaining why the fix is unavailable, if any
35177             this.icon = attrs.icon;                     // Optional - shows 'iD-icon-wrench' if not set
35178             this.entityIds = attrs.entityIds || [];     // Optional - used for hover-higlighting.
35179             this.autoArgs = attrs.autoArgs;             // Optional - pass [actions, annotation] arglist if this fix can automatically run
35180
35181             this.issue = null;    // Generated link - added by validationIssue
35182         }
35183
35184         var buildRuleChecks = function() {
35185             return {
35186                 equals: function (equals) {
35187                     return function(tags) {
35188                         return Object.keys(equals).every(function(k) {
35189                             return equals[k] === tags[k];
35190                         });
35191                     };
35192                 },
35193                 notEquals: function (notEquals) {
35194                     return function(tags) {
35195                         return Object.keys(notEquals).some(function(k) {
35196                             return notEquals[k] !== tags[k];
35197                         });
35198                     };
35199                 },
35200                 absence: function(absence) {
35201                     return function(tags) {
35202                         return Object.keys(tags).indexOf(absence) === -1;
35203                     };
35204                 },
35205                 presence: function(presence) {
35206                     return function(tags) {
35207                         return Object.keys(tags).indexOf(presence) > -1;
35208                     };
35209                 },
35210                 greaterThan: function(greaterThan) {
35211                     var key = Object.keys(greaterThan)[0];
35212                     var value = greaterThan[key];
35213
35214                     return function(tags) {
35215                         return tags[key] > value;
35216                     };
35217                 },
35218                 greaterThanEqual: function(greaterThanEqual) {
35219                     var key = Object.keys(greaterThanEqual)[0];
35220                     var value = greaterThanEqual[key];
35221
35222                     return function(tags) {
35223                         return tags[key] >= value;
35224                     };
35225                 },
35226                 lessThan: function(lessThan) {
35227                     var key = Object.keys(lessThan)[0];
35228                     var value = lessThan[key];
35229
35230                     return function(tags) {
35231                         return tags[key] < value;
35232                     };
35233                 },
35234                 lessThanEqual: function(lessThanEqual) {
35235                     var key = Object.keys(lessThanEqual)[0];
35236                     var value = lessThanEqual[key];
35237
35238                     return function(tags) {
35239                         return tags[key] <= value;
35240                     };
35241                 },
35242                 positiveRegex: function(positiveRegex) {
35243                     var tagKey = Object.keys(positiveRegex)[0];
35244                     var expression = positiveRegex[tagKey].join('|');
35245                     var regex = new RegExp(expression);
35246
35247                     return function(tags) {
35248                         return regex.test(tags[tagKey]);
35249                     };
35250                 },
35251                 negativeRegex: function(negativeRegex) {
35252                     var tagKey = Object.keys(negativeRegex)[0];
35253                     var expression = negativeRegex[tagKey].join('|');
35254                     var regex = new RegExp(expression);
35255
35256                     return function(tags) {
35257                         return !regex.test(tags[tagKey]);
35258                     };
35259                 }
35260             };
35261         };
35262
35263         var buildLineKeys = function() {
35264             return {
35265                 highway: {
35266                     rest_area: true,
35267                     services: true
35268                 },
35269                 railway: {
35270                     roundhouse: true,
35271                     station: true,
35272                     traverser: true,
35273                     turntable: true,
35274                     wash: true
35275                 }
35276             };
35277         };
35278
35279         var serviceMapRules = {
35280             init: function() {
35281                 this._ruleChecks  = buildRuleChecks();
35282                 this._validationRules = [];
35283                 this._areaKeys = osmAreaKeys;
35284                 this._lineKeys = buildLineKeys();
35285             },
35286
35287             // list of rules only relevant to tag checks...
35288             filterRuleChecks: function(selector) {
35289                 var _ruleChecks = this._ruleChecks;
35290                 return Object.keys(selector).reduce(function(rules, key) {
35291                     if (['geometry', 'error', 'warning'].indexOf(key) === -1) {
35292                         rules.push(_ruleChecks[key](selector[key]));
35293                     }
35294                     return rules;
35295                 }, []);
35296             },
35297
35298             // builds tagMap from mapcss-parse selector object...
35299             buildTagMap: function(selector) {
35300                 var getRegexValues = function(regexes) {
35301                     return regexes.map(function(regex) {
35302                         return regex.replace(/\$|\^/g, '');
35303                     });
35304                 };
35305
35306                 var tagMap = Object.keys(selector).reduce(function (expectedTags, key) {
35307                     var values;
35308                     var isRegex = /regex/gi.test(key);
35309                     var isEqual = /equals/gi.test(key);
35310
35311                     if (isRegex || isEqual) {
35312                         Object.keys(selector[key]).forEach(function(selectorKey) {
35313                             values = isEqual ? [selector[key][selectorKey]] : getRegexValues(selector[key][selectorKey]);
35314
35315                             if (expectedTags.hasOwnProperty(selectorKey)) {
35316                                 values = values.concat(expectedTags[selectorKey]);
35317                             }
35318
35319                             expectedTags[selectorKey] = values;
35320                         });
35321
35322                     } else if (/(greater|less)Than(Equal)?|presence/g.test(key)) {
35323                         var tagKey = /presence/.test(key) ? selector[key] : Object.keys(selector[key])[0];
35324
35325                         values = [selector[key][tagKey]];
35326
35327                         if (expectedTags.hasOwnProperty(tagKey)) {
35328                             values = values.concat(expectedTags[tagKey]);
35329                         }
35330
35331                         expectedTags[tagKey] = values;
35332                     }
35333
35334                     return expectedTags;
35335                 }, {});
35336
35337                 return tagMap;
35338             },
35339
35340             // inspired by osmWay#isArea()
35341             inferGeometry: function(tagMap) {
35342                 var _lineKeys = this._lineKeys;
35343                 var _areaKeys = this._areaKeys;
35344
35345                 var keyValueDoesNotImplyArea = function(key) {
35346                     return utilArrayIntersection(tagMap[key], Object.keys(_areaKeys[key])).length > 0;
35347                 };
35348                 var keyValueImpliesLine = function(key) {
35349                     return utilArrayIntersection(tagMap[key], Object.keys(_lineKeys[key])).length > 0;
35350                 };
35351
35352                 if (tagMap.hasOwnProperty('area')) {
35353                     if (tagMap.area.indexOf('yes') > -1) {
35354                         return 'area';
35355                     }
35356                     if (tagMap.area.indexOf('no') > -1) {
35357                         return 'line';
35358                     }
35359                 }
35360
35361                 for (var key in tagMap) {
35362                     if (key in _areaKeys && !keyValueDoesNotImplyArea(key)) {
35363                         return 'area';
35364                     }
35365                     if (key in _lineKeys && keyValueImpliesLine(key)) {
35366                         return 'area';
35367                     }
35368                 }
35369
35370                 return 'line';
35371             },
35372
35373             // adds from mapcss-parse selector check...
35374             addRule: function(selector) {
35375                 var rule = {
35376                     // checks relevant to mapcss-selector
35377                     checks: this.filterRuleChecks(selector),
35378                     // true if all conditions for a tag error are true..
35379                     matches: function(entity) {
35380                         return this.checks.every(function(check) {
35381                             return check(entity.tags);
35382                         });
35383                     },
35384                     // borrowed from Way#isArea()
35385                     inferredGeometry: this.inferGeometry(this.buildTagMap(selector), this._areaKeys),
35386                     geometryMatches: function(entity, graph) {
35387                         if (entity.type === 'node' || entity.type === 'relation') {
35388                             return selector.geometry === entity.type;
35389                         } else if (entity.type === 'way') {
35390                             return this.inferredGeometry === entity.geometry(graph);
35391                         }
35392                     },
35393                     // when geometries match and tag matches are present, return a warning...
35394                     findIssues: function (entity, graph, issues) {
35395                         if (this.geometryMatches(entity, graph) && this.matches(entity)) {
35396                             var severity = Object.keys(selector).indexOf('error') > -1
35397                                     ? 'error'
35398                                     : 'warning';
35399                             var message = selector[severity];
35400                             issues.push(new validationIssue({
35401                                 type: 'maprules',
35402                                 severity: severity,
35403                                 message: function() {
35404                                     return message;
35405                                 },
35406                                 entityIds: [entity.id]
35407                             }));
35408                         }
35409                     }
35410                 };
35411                 this._validationRules.push(rule);
35412             },
35413
35414             clearRules: function() { this._validationRules = []; },
35415
35416             // returns validationRules...
35417             validationRules: function() { return this._validationRules; },
35418
35419             // returns ruleChecks
35420             ruleChecks: function() { return this._ruleChecks; }
35421         };
35422
35423         var apibase$1 = 'https://nominatim.openstreetmap.org/';
35424         var _inflight = {};
35425         var _nominatimCache;
35426
35427
35428         var serviceNominatim = {
35429
35430             init: function() {
35431                 _inflight = {};
35432                 _nominatimCache = new RBush();
35433             },
35434
35435             reset: function() {
35436                 Object.values(_inflight).forEach(function(controller) { controller.abort(); });
35437                 _inflight = {};
35438                 _nominatimCache = new RBush();
35439             },
35440
35441
35442             countryCode: function (location, callback) {
35443                 this.reverse(location, function(err, result) {
35444                     if (err) {
35445                         return callback(err);
35446                     } else if (result.address) {
35447                         return callback(null, result.address.country_code);
35448                     } else {
35449                         return callback('Unable to geocode', null);
35450                     }
35451                 });
35452             },
35453
35454
35455             reverse: function (loc, callback) {
35456                 var cached = _nominatimCache.search(
35457                     { minX: loc[0], minY: loc[1], maxX: loc[0], maxY: loc[1] }
35458                 );
35459
35460                 if (cached.length > 0) {
35461                     if (callback) { callback(null, cached[0].data); }
35462                     return;
35463                 }
35464
35465                 var params = { zoom: 13, format: 'json', addressdetails: 1, lat: loc[1], lon: loc[0] };
35466                 var url = apibase$1 + 'reverse?' + utilQsString(params);
35467
35468                 if (_inflight[url]) { return; }
35469                 var controller = new AbortController();
35470                 _inflight[url] = controller;
35471
35472                 d3_json(url, { signal: controller.signal })
35473                     .then(function(result) {
35474                         delete _inflight[url];
35475                         if (result && result.error) {
35476                             throw new Error(result.error);
35477                         }
35478                         var extent = geoExtent(loc).padByMeters(200);
35479                         _nominatimCache.insert(Object.assign(extent.bbox(), {data: result}));
35480                         if (callback) { callback(null, result); }
35481                     })
35482                     .catch(function(err) {
35483                         delete _inflight[url];
35484                         if (err.name === 'AbortError') { return; }
35485                         if (callback) { callback(err.message); }
35486                     });
35487             },
35488
35489
35490             search: function (val, callback) {
35491                 var searchVal = encodeURIComponent(val);
35492                 var url = apibase$1 + 'search/' + searchVal + '?limit=10&format=json';
35493
35494                 if (_inflight[url]) { return; }
35495                 var controller = new AbortController();
35496                 _inflight[url] = controller;
35497
35498                 d3_json(url, { signal: controller.signal })
35499                     .then(function(result) {
35500                         delete _inflight[url];
35501                         if (result && result.error) {
35502                             throw new Error(result.error);
35503                         }
35504                         if (callback) { callback(null, result); }
35505                     })
35506                     .catch(function(err) {
35507                         delete _inflight[url];
35508                         if (err.name === 'AbortError') { return; }
35509                         if (callback) { callback(err.message); }
35510                     });
35511             }
35512
35513         };
35514
35515         var apibase$2 = 'https://openstreetcam.org';
35516         var maxResults$1 = 1000;
35517         var tileZoom$1 = 14;
35518         var tiler$4 = utilTiler().zoomExtent([tileZoom$1, tileZoom$1]).skipNullIsland(true);
35519         var dispatch$5 = dispatch('loadedImages');
35520         var imgZoom = d3_zoom()
35521             .extent([[0, 0], [320, 240]])
35522             .translateExtent([[0, 0], [320, 240]])
35523             .scaleExtent([1, 15]);
35524         var _oscCache;
35525         var _oscSelectedImage;
35526
35527
35528         function abortRequest$4(controller) {
35529             controller.abort();
35530         }
35531
35532
35533         function maxPageAtZoom$1(z) {
35534             if (z < 15)   { return 2; }
35535             if (z === 15) { return 5; }
35536             if (z === 16) { return 10; }
35537             if (z === 17) { return 20; }
35538             if (z === 18) { return 40; }
35539             if (z > 18)   { return 80; }
35540         }
35541
35542
35543         function loadTiles$1(which, url, projection) {
35544             var currZoom = Math.floor(geoScaleToZoom(projection.scale()));
35545             var tiles = tiler$4.getTiles(projection);
35546
35547             // abort inflight requests that are no longer needed
35548             var cache = _oscCache[which];
35549             Object.keys(cache.inflight).forEach(function(k) {
35550                 var wanted = tiles.find(function(tile) { return k.indexOf(tile.id + ',') === 0; });
35551                 if (!wanted) {
35552                     abortRequest$4(cache.inflight[k]);
35553                     delete cache.inflight[k];
35554                 }
35555             });
35556
35557             tiles.forEach(function(tile) {
35558                 loadNextTilePage$1(which, currZoom, url, tile);
35559             });
35560         }
35561
35562
35563         function loadNextTilePage$1(which, currZoom, url, tile) {
35564             var cache = _oscCache[which];
35565             var bbox = tile.extent.bbox();
35566             var maxPages = maxPageAtZoom$1(currZoom);
35567             var nextPage = cache.nextPage[tile.id] || 1;
35568             var params = utilQsString({
35569                 ipp: maxResults$1,
35570                 page: nextPage,
35571                 // client_id: clientId,
35572                 bbTopLeft: [bbox.maxY, bbox.minX].join(','),
35573                 bbBottomRight: [bbox.minY, bbox.maxX].join(',')
35574             }, true);
35575
35576             if (nextPage > maxPages) { return; }
35577
35578             var id = tile.id + ',' + String(nextPage);
35579             if (cache.loaded[id] || cache.inflight[id]) { return; }
35580
35581             var controller = new AbortController();
35582             cache.inflight[id] = controller;
35583
35584             var options = {
35585                 method: 'POST',
35586                 signal: controller.signal,
35587                 body: params,
35588                 headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
35589             };
35590
35591             d3_json(url, options)
35592                 .then(function(data) {
35593                     cache.loaded[id] = true;
35594                     delete cache.inflight[id];
35595                     if (!data || !data.currentPageItems || !data.currentPageItems.length) {
35596                         throw new Error('No Data');
35597                     }
35598
35599                     var features = data.currentPageItems.map(function(item) {
35600                         var loc = [+item.lng, +item.lat];
35601                         var d;
35602
35603                         if (which === 'images') {
35604                             d = {
35605                                 loc: loc,
35606                                 key: item.id,
35607                                 ca: +item.heading,
35608                                 captured_at: (item.shot_date || item.date_added),
35609                                 captured_by: item.username,
35610                                 imagePath: item.lth_name,
35611                                 sequence_id: item.sequence_id,
35612                                 sequence_index: +item.sequence_index
35613                             };
35614
35615                             // cache sequence info
35616                             var seq = _oscCache.sequences[d.sequence_id];
35617                             if (!seq) {
35618                                 seq = { rotation: 0, images: [] };
35619                                 _oscCache.sequences[d.sequence_id] = seq;
35620                             }
35621                             seq.images[d.sequence_index] = d;
35622                         }
35623
35624                         return {
35625                             minX: loc[0], minY: loc[1], maxX: loc[0], maxY: loc[1], data: d
35626                         };
35627                     });
35628
35629                     cache.rtree.load(features);
35630
35631                     if (data.currentPageItems.length === maxResults$1) {  // more pages to load
35632                         cache.nextPage[tile.id] = nextPage + 1;
35633                         loadNextTilePage$1(which, currZoom, url, tile);
35634                     } else {
35635                         cache.nextPage[tile.id] = Infinity;     // no more pages to load
35636                     }
35637
35638                     if (which === 'images') {
35639                         dispatch$5.call('loadedImages');
35640                     }
35641                 })
35642                 .catch(function() {
35643                     cache.loaded[id] = true;
35644                     delete cache.inflight[id];
35645                 });
35646         }
35647
35648
35649         // partition viewport into higher zoom tiles
35650         function partitionViewport$1(projection) {
35651             var z = geoScaleToZoom(projection.scale());
35652             var z2 = (Math.ceil(z * 2) / 2) + 2.5;   // round to next 0.5 and add 2.5
35653             var tiler = utilTiler().zoomExtent([z2, z2]);
35654
35655             return tiler.getTiles(projection)
35656                 .map(function(tile) { return tile.extent; });
35657         }
35658
35659
35660         // no more than `limit` results per partition.
35661         function searchLimited$1(limit, projection, rtree) {
35662             limit = limit || 5;
35663
35664             return partitionViewport$1(projection)
35665                 .reduce(function(result, extent) {
35666                     var found = rtree.search(extent.bbox())
35667                         .slice(0, limit)
35668                         .map(function(d) { return d.data; });
35669
35670                     return (found.length ? result.concat(found) : result);
35671                 }, []);
35672         }
35673
35674
35675         var serviceOpenstreetcam = {
35676
35677             init: function() {
35678                 if (!_oscCache) {
35679                     this.reset();
35680                 }
35681
35682                 this.event = utilRebind(this, dispatch$5, 'on');
35683             },
35684
35685             reset: function() {
35686                 if (_oscCache) {
35687                     Object.values(_oscCache.images.inflight).forEach(abortRequest$4);
35688                 }
35689
35690                 _oscCache = {
35691                     images: { inflight: {}, loaded: {}, nextPage: {}, rtree: new RBush() },
35692                     sequences: {}
35693                 };
35694
35695                 _oscSelectedImage = null;
35696             },
35697
35698
35699             images: function(projection) {
35700                 var limit = 5;
35701                 return searchLimited$1(limit, projection, _oscCache.images.rtree);
35702             },
35703
35704
35705             sequences: function(projection) {
35706                 var viewport = projection.clipExtent();
35707                 var min = [viewport[0][0], viewport[1][1]];
35708                 var max = [viewport[1][0], viewport[0][1]];
35709                 var bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox();
35710                 var sequenceKeys = {};
35711
35712                 // all sequences for images in viewport
35713                 _oscCache.images.rtree.search(bbox)
35714                     .forEach(function(d) { sequenceKeys[d.data.sequence_id] = true; });
35715
35716                 // make linestrings from those sequences
35717                 var lineStrings = [];
35718                 Object.keys(sequenceKeys)
35719                     .forEach(function(sequenceKey) {
35720                         var seq = _oscCache.sequences[sequenceKey];
35721                         var images = seq && seq.images;
35722                         if (images) {
35723                             lineStrings.push({
35724                                 type: 'LineString',
35725                                 coordinates: images.map(function (d) { return d.loc; }).filter(Boolean),
35726                                 properties: { key: sequenceKey }
35727                             });
35728                         }
35729                     });
35730                 return lineStrings;
35731             },
35732
35733
35734             loadImages: function(projection) {
35735                 var url = apibase$2 + '/1.0/list/nearby-photos/';
35736                 loadTiles$1('images', url, projection);
35737             },
35738
35739
35740             loadViewer: function(context) {
35741                 var that = this;
35742
35743                 // add osc-wrapper
35744                 var wrap = context.container().select('.photoviewer').selectAll('.osc-wrapper')
35745                     .data([0]);
35746
35747                 var wrapEnter = wrap.enter()
35748                     .append('div')
35749                     .attr('class', 'photo-wrapper osc-wrapper')
35750                     .classed('hide', true)
35751                     .call(imgZoom.on('zoom', zoomPan))
35752                     .on('dblclick.zoom', null);
35753
35754                 wrapEnter
35755                     .append('div')
35756                     .attr('class', 'photo-attribution fillD');
35757
35758                 var controlsEnter = wrapEnter
35759                     .append('div')
35760                     .attr('class', 'photo-controls-wrap')
35761                     .append('div')
35762                     .attr('class', 'photo-controls');
35763
35764                 controlsEnter
35765                     .append('button')
35766                     .on('click.back', step(-1))
35767                     .text('◄');
35768
35769                 controlsEnter
35770                     .append('button')
35771                     .on('click.rotate-ccw', rotate(-90))
35772                     .text('⤿');
35773
35774                 controlsEnter
35775                     .append('button')
35776                     .on('click.rotate-cw', rotate(90))
35777                     .text('⤾');
35778
35779                 controlsEnter
35780                     .append('button')
35781                     .on('click.forward', step(1))
35782                     .text('►');
35783
35784                 wrapEnter
35785                     .append('div')
35786                     .attr('class', 'osc-image-wrap');
35787
35788
35789                 // Register viewer resize handler
35790                 context.ui().photoviewer.on('resize.openstreetcam', function(dimensions) {
35791                     imgZoom = d3_zoom()
35792                         .extent([[0, 0], dimensions])
35793                         .translateExtent([[0, 0], dimensions])
35794                         .scaleExtent([1, 15])
35795                         .on('zoom', zoomPan);
35796                 });
35797
35798
35799                 function zoomPan() {
35800                     var t = event.transform;
35801                     context.container().select('.photoviewer .osc-image-wrap')
35802                         .call(utilSetTransform, t.x, t.y, t.k);
35803                 }
35804
35805
35806                 function rotate(deg) {
35807                     return function() {
35808                         if (!_oscSelectedImage) { return; }
35809                         var sequenceKey = _oscSelectedImage.sequence_id;
35810                         var sequence = _oscCache.sequences[sequenceKey];
35811                         if (!sequence) { return; }
35812
35813                         var r = sequence.rotation || 0;
35814                         r += deg;
35815
35816                         if (r > 180) { r -= 360; }
35817                         if (r < -180) { r += 360; }
35818                         sequence.rotation = r;
35819
35820                         var wrap = context.container().select('.photoviewer .osc-wrapper');
35821
35822                         wrap
35823                             .transition()
35824                             .duration(100)
35825                             .call(imgZoom.transform, identity$2);
35826
35827                         wrap.selectAll('.osc-image')
35828                             .transition()
35829                             .duration(100)
35830                             .style('transform', 'rotate(' + r + 'deg)');
35831                     };
35832                 }
35833
35834                 function step(stepBy) {
35835                     return function() {
35836                         if (!_oscSelectedImage) { return; }
35837                         var sequenceKey = _oscSelectedImage.sequence_id;
35838                         var sequence = _oscCache.sequences[sequenceKey];
35839                         if (!sequence) { return; }
35840
35841                         var nextIndex = _oscSelectedImage.sequence_index + stepBy;
35842                         var nextImage = sequence.images[nextIndex];
35843                         if (!nextImage) { return; }
35844
35845                         context.map().centerEase(nextImage.loc);
35846
35847                         that
35848                             .selectImage(context, nextImage)
35849                             .updateViewer(context, nextImage);
35850                     };
35851                 }
35852             },
35853
35854
35855             showViewer: function(context) {
35856                 var viewer = context.container().select('.photoviewer')
35857                     .classed('hide', false);
35858
35859                 var isHidden = viewer.selectAll('.photo-wrapper.osc-wrapper.hide').size();
35860
35861                 if (isHidden) {
35862                     viewer
35863                         .selectAll('.photo-wrapper:not(.osc-wrapper)')
35864                         .classed('hide', true);
35865
35866                     viewer
35867                         .selectAll('.photo-wrapper.osc-wrapper')
35868                         .classed('hide', false);
35869                 }
35870
35871                 return this;
35872             },
35873
35874
35875             hideViewer: function(context) {
35876                 _oscSelectedImage = null;
35877
35878                 var viewer = context.container().select('.photoviewer');
35879                 if (!viewer.empty()) { viewer.datum(null); }
35880
35881                 viewer
35882                     .classed('hide', true)
35883                     .selectAll('.photo-wrapper')
35884                     .classed('hide', true);
35885
35886                 context.container().selectAll('.viewfield-group, .sequence, .icon-sign')
35887                     .classed('currentView', false);
35888
35889                 return this.setStyles(context, null, true);
35890             },
35891
35892
35893             updateViewer: function(context, d) {
35894                 var wrap = context.container().select('.photoviewer .osc-wrapper');
35895                 var imageWrap = wrap.selectAll('.osc-image-wrap');
35896                 var attribution = wrap.selectAll('.photo-attribution').html('');
35897
35898                 wrap
35899                     .transition()
35900                     .duration(100)
35901                     .call(imgZoom.transform, identity$2);
35902
35903                 imageWrap
35904                     .selectAll('.osc-image')
35905                     .remove();
35906
35907                 if (d) {
35908                     var sequence = _oscCache.sequences[d.sequence_id];
35909                     var r = (sequence && sequence.rotation) || 0;
35910
35911                     imageWrap
35912                         .append('img')
35913                         .attr('class', 'osc-image')
35914                         .attr('src', apibase$2 + '/' + d.imagePath)
35915                         .style('transform', 'rotate(' + r + 'deg)');
35916
35917                     if (d.captured_by) {
35918                         attribution
35919                             .append('a')
35920                             .attr('class', 'captured_by')
35921                             .attr('target', '_blank')
35922                             .attr('href', 'https://openstreetcam.org/user/' + encodeURIComponent(d.captured_by))
35923                             .text('@' + d.captured_by);
35924
35925                         attribution
35926                             .append('span')
35927                             .text('|');
35928                     }
35929
35930                     if (d.captured_at) {
35931                         attribution
35932                             .append('span')
35933                             .attr('class', 'captured_at')
35934                             .text(localeDateString(d.captured_at));
35935
35936                         attribution
35937                             .append('span')
35938                             .text('|');
35939                     }
35940
35941                     attribution
35942                         .append('a')
35943                         .attr('class', 'image-link')
35944                         .attr('target', '_blank')
35945                         .attr('href', 'https://openstreetcam.org/details/' + d.sequence_id + '/' + d.sequence_index)
35946                         .text('openstreetcam.org');
35947                 }
35948
35949                 return this;
35950
35951
35952                 function localeDateString(s) {
35953                     if (!s) { return null; }
35954                     var options = { day: 'numeric', month: 'short', year: 'numeric' };
35955                     var d = new Date(s);
35956                     if (isNaN(d.getTime())) { return null; }
35957                     return d.toLocaleDateString(_mainLocalizer.localeCode(), options);
35958                 }
35959             },
35960
35961
35962             selectImage: function(context, d) {
35963                 _oscSelectedImage = d;
35964                 var viewer = context.container().select('.photoviewer');
35965                 if (!viewer.empty()) { viewer.datum(d); }
35966
35967                 this.setStyles(context, null, true);
35968
35969                 context.container().selectAll('.icon-sign')
35970                     .classed('currentView', false);
35971
35972                 return this;
35973             },
35974
35975
35976             getSelectedImage: function() {
35977                 return _oscSelectedImage;
35978             },
35979
35980
35981             getSequenceKeyForImage: function(d) {
35982                 return d && d.sequence_id;
35983             },
35984
35985
35986             // Updates the currently highlighted sequence and selected bubble.
35987             // Reset is only necessary when interacting with the viewport because
35988             // this implicitly changes the currently selected bubble/sequence
35989             setStyles: function(context, hovered, reset) {
35990                 if (reset) {  // reset all layers
35991                     context.container().selectAll('.viewfield-group')
35992                         .classed('highlighted', false)
35993                         .classed('hovered', false)
35994                         .classed('currentView', false);
35995
35996                     context.container().selectAll('.sequence')
35997                         .classed('highlighted', false)
35998                         .classed('currentView', false);
35999                 }
36000
36001                 var hoveredImageKey = hovered && hovered.key;
36002                 var hoveredSequenceKey = this.getSequenceKeyForImage(hovered);
36003                 var hoveredSequence = hoveredSequenceKey && _oscCache.sequences[hoveredSequenceKey];
36004                 var hoveredImageKeys = (hoveredSequence && hoveredSequence.images.map(function (d) { return d.key; })) || [];
36005
36006                 var viewer = context.container().select('.photoviewer');
36007                 var selected = viewer.empty() ? undefined : viewer.datum();
36008                 var selectedImageKey = selected && selected.key;
36009                 var selectedSequenceKey = this.getSequenceKeyForImage(selected);
36010                 var selectedSequence = selectedSequenceKey && _oscCache.sequences[selectedSequenceKey];
36011                 var selectedImageKeys = (selectedSequence && selectedSequence.images.map(function (d) { return d.key; })) || [];
36012
36013                 // highlight sibling viewfields on either the selected or the hovered sequences
36014                 var highlightedImageKeys = utilArrayUnion(hoveredImageKeys, selectedImageKeys);
36015
36016                 context.container().selectAll('.layer-openstreetcam .viewfield-group')
36017                     .classed('highlighted', function(d) { return highlightedImageKeys.indexOf(d.key) !== -1; })
36018                     .classed('hovered', function(d) { return d.key === hoveredImageKey; })
36019                     .classed('currentView', function(d) { return d.key === selectedImageKey; });
36020
36021                 context.container().selectAll('.layer-openstreetcam .sequence')
36022                     .classed('highlighted', function(d) { return d.properties.key === hoveredSequenceKey; })
36023                     .classed('currentView', function(d) { return d.properties.key === selectedSequenceKey; });
36024
36025                 // update viewfields if needed
36026                 context.container().selectAll('.viewfield-group .viewfield')
36027                     .attr('d', viewfieldPath);
36028
36029                 function viewfieldPath() {
36030                     var d = this.parentNode.__data__;
36031                     if (d.pano && d.key !== selectedImageKey) {
36032                         return 'M 8,13 m -10,0 a 10,10 0 1,0 20,0 a 10,10 0 1,0 -20,0';
36033                     } else {
36034                         return 'M 6,9 C 8,8.4 8,8.4 10,9 L 16,-2 C 12,-5 4,-5 0,-2 z';
36035                     }
36036                 }
36037
36038                 return this;
36039             },
36040
36041
36042             cache: function() {
36043                 return _oscCache;
36044             }
36045
36046         };
36047
36048         /**
36049          * Checks if `value` is the
36050          * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types)
36051          * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)
36052          *
36053          * @static
36054          * @memberOf _
36055          * @since 0.1.0
36056          * @category Lang
36057          * @param {*} value The value to check.
36058          * @returns {boolean} Returns `true` if `value` is an object, else `false`.
36059          * @example
36060          *
36061          * _.isObject({});
36062          * // => true
36063          *
36064          * _.isObject([1, 2, 3]);
36065          * // => true
36066          *
36067          * _.isObject(_.noop);
36068          * // => true
36069          *
36070          * _.isObject(null);
36071          * // => false
36072          */
36073         function isObject$1(value) {
36074           var type = typeof value;
36075           return value != null && (type == 'object' || type == 'function');
36076         }
36077
36078         /** Detect free variable `global` from Node.js. */
36079         var freeGlobal = typeof global == 'object' && global && global.Object === Object && global;
36080
36081         /** Detect free variable `self`. */
36082         var freeSelf = typeof self == 'object' && self && self.Object === Object && self;
36083
36084         /** Used as a reference to the global object. */
36085         var root$2 = freeGlobal || freeSelf || Function('return this')();
36086
36087         /**
36088          * Gets the timestamp of the number of milliseconds that have elapsed since
36089          * the Unix epoch (1 January 1970 00:00:00 UTC).
36090          *
36091          * @static
36092          * @memberOf _
36093          * @since 2.4.0
36094          * @category Date
36095          * @returns {number} Returns the timestamp.
36096          * @example
36097          *
36098          * _.defer(function(stamp) {
36099          *   console.log(_.now() - stamp);
36100          * }, _.now());
36101          * // => Logs the number of milliseconds it took for the deferred invocation.
36102          */
36103         var now$1 = function() {
36104           return root$2.Date.now();
36105         };
36106
36107         /** Built-in value references. */
36108         var Symbol$1 = root$2.Symbol;
36109
36110         /** Used for built-in method references. */
36111         var objectProto = Object.prototype;
36112
36113         /** Used to check objects for own properties. */
36114         var hasOwnProperty$2 = objectProto.hasOwnProperty;
36115
36116         /**
36117          * Used to resolve the
36118          * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)
36119          * of values.
36120          */
36121         var nativeObjectToString = objectProto.toString;
36122
36123         /** Built-in value references. */
36124         var symToStringTag = Symbol$1 ? Symbol$1.toStringTag : undefined;
36125
36126         /**
36127          * A specialized version of `baseGetTag` which ignores `Symbol.toStringTag` values.
36128          *
36129          * @private
36130          * @param {*} value The value to query.
36131          * @returns {string} Returns the raw `toStringTag`.
36132          */
36133         function getRawTag(value) {
36134           var isOwn = hasOwnProperty$2.call(value, symToStringTag),
36135               tag = value[symToStringTag];
36136
36137           try {
36138             value[symToStringTag] = undefined;
36139             var unmasked = true;
36140           } catch (e) {}
36141
36142           var result = nativeObjectToString.call(value);
36143           if (unmasked) {
36144             if (isOwn) {
36145               value[symToStringTag] = tag;
36146             } else {
36147               delete value[symToStringTag];
36148             }
36149           }
36150           return result;
36151         }
36152
36153         /** Used for built-in method references. */
36154         var objectProto$1 = Object.prototype;
36155
36156         /**
36157          * Used to resolve the
36158          * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)
36159          * of values.
36160          */
36161         var nativeObjectToString$1 = objectProto$1.toString;
36162
36163         /**
36164          * Converts `value` to a string using `Object.prototype.toString`.
36165          *
36166          * @private
36167          * @param {*} value The value to convert.
36168          * @returns {string} Returns the converted string.
36169          */
36170         function objectToString$2(value) {
36171           return nativeObjectToString$1.call(value);
36172         }
36173
36174         /** `Object#toString` result references. */
36175         var nullTag = '[object Null]',
36176             undefinedTag = '[object Undefined]';
36177
36178         /** Built-in value references. */
36179         var symToStringTag$1 = Symbol$1 ? Symbol$1.toStringTag : undefined;
36180
36181         /**
36182          * The base implementation of `getTag` without fallbacks for buggy environments.
36183          *
36184          * @private
36185          * @param {*} value The value to query.
36186          * @returns {string} Returns the `toStringTag`.
36187          */
36188         function baseGetTag(value) {
36189           if (value == null) {
36190             return value === undefined ? undefinedTag : nullTag;
36191           }
36192           return (symToStringTag$1 && symToStringTag$1 in Object(value))
36193             ? getRawTag(value)
36194             : objectToString$2(value);
36195         }
36196
36197         /**
36198          * Checks if `value` is object-like. A value is object-like if it's not `null`
36199          * and has a `typeof` result of "object".
36200          *
36201          * @static
36202          * @memberOf _
36203          * @since 4.0.0
36204          * @category Lang
36205          * @param {*} value The value to check.
36206          * @returns {boolean} Returns `true` if `value` is object-like, else `false`.
36207          * @example
36208          *
36209          * _.isObjectLike({});
36210          * // => true
36211          *
36212          * _.isObjectLike([1, 2, 3]);
36213          * // => true
36214          *
36215          * _.isObjectLike(_.noop);
36216          * // => false
36217          *
36218          * _.isObjectLike(null);
36219          * // => false
36220          */
36221         function isObjectLike(value) {
36222           return value != null && typeof value == 'object';
36223         }
36224
36225         /** `Object#toString` result references. */
36226         var symbolTag = '[object Symbol]';
36227
36228         /**
36229          * Checks if `value` is classified as a `Symbol` primitive or object.
36230          *
36231          * @static
36232          * @memberOf _
36233          * @since 4.0.0
36234          * @category Lang
36235          * @param {*} value The value to check.
36236          * @returns {boolean} Returns `true` if `value` is a symbol, else `false`.
36237          * @example
36238          *
36239          * _.isSymbol(Symbol.iterator);
36240          * // => true
36241          *
36242          * _.isSymbol('abc');
36243          * // => false
36244          */
36245         function isSymbol$4(value) {
36246           return typeof value == 'symbol' ||
36247             (isObjectLike(value) && baseGetTag(value) == symbolTag);
36248         }
36249
36250         /** Used as references for various `Number` constants. */
36251         var NAN = 0 / 0;
36252
36253         /** Used to match leading and trailing whitespace. */
36254         var reTrim = /^\s+|\s+$/g;
36255
36256         /** Used to detect bad signed hexadecimal string values. */
36257         var reIsBadHex = /^[-+]0x[0-9a-f]+$/i;
36258
36259         /** Used to detect binary string values. */
36260         var reIsBinary = /^0b[01]+$/i;
36261
36262         /** Used to detect octal string values. */
36263         var reIsOctal = /^0o[0-7]+$/i;
36264
36265         /** Built-in method references without a dependency on `root`. */
36266         var freeParseInt = parseInt;
36267
36268         /**
36269          * Converts `value` to a number.
36270          *
36271          * @static
36272          * @memberOf _
36273          * @since 4.0.0
36274          * @category Lang
36275          * @param {*} value The value to process.
36276          * @returns {number} Returns the number.
36277          * @example
36278          *
36279          * _.toNumber(3.2);
36280          * // => 3.2
36281          *
36282          * _.toNumber(Number.MIN_VALUE);
36283          * // => 5e-324
36284          *
36285          * _.toNumber(Infinity);
36286          * // => Infinity
36287          *
36288          * _.toNumber('3.2');
36289          * // => 3.2
36290          */
36291         function toNumber(value) {
36292           if (typeof value == 'number') {
36293             return value;
36294           }
36295           if (isSymbol$4(value)) {
36296             return NAN;
36297           }
36298           if (isObject$1(value)) {
36299             var other = typeof value.valueOf == 'function' ? value.valueOf() : value;
36300             value = isObject$1(other) ? (other + '') : other;
36301           }
36302           if (typeof value != 'string') {
36303             return value === 0 ? value : +value;
36304           }
36305           value = value.replace(reTrim, '');
36306           var isBinary = reIsBinary.test(value);
36307           return (isBinary || reIsOctal.test(value))
36308             ? freeParseInt(value.slice(2), isBinary ? 2 : 8)
36309             : (reIsBadHex.test(value) ? NAN : +value);
36310         }
36311
36312         /** Error message constants. */
36313         var FUNC_ERROR_TEXT = 'Expected a function';
36314
36315         /* Built-in method references for those with the same name as other `lodash` methods. */
36316         var nativeMax = Math.max,
36317             nativeMin = Math.min;
36318
36319         /**
36320          * Creates a debounced function that delays invoking `func` until after `wait`
36321          * milliseconds have elapsed since the last time the debounced function was
36322          * invoked. The debounced function comes with a `cancel` method to cancel
36323          * delayed `func` invocations and a `flush` method to immediately invoke them.
36324          * Provide `options` to indicate whether `func` should be invoked on the
36325          * leading and/or trailing edge of the `wait` timeout. The `func` is invoked
36326          * with the last arguments provided to the debounced function. Subsequent
36327          * calls to the debounced function return the result of the last `func`
36328          * invocation.
36329          *
36330          * **Note:** If `leading` and `trailing` options are `true`, `func` is
36331          * invoked on the trailing edge of the timeout only if the debounced function
36332          * is invoked more than once during the `wait` timeout.
36333          *
36334          * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred
36335          * until to the next tick, similar to `setTimeout` with a timeout of `0`.
36336          *
36337          * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)
36338          * for details over the differences between `_.debounce` and `_.throttle`.
36339          *
36340          * @static
36341          * @memberOf _
36342          * @since 0.1.0
36343          * @category Function
36344          * @param {Function} func The function to debounce.
36345          * @param {number} [wait=0] The number of milliseconds to delay.
36346          * @param {Object} [options={}] The options object.
36347          * @param {boolean} [options.leading=false]
36348          *  Specify invoking on the leading edge of the timeout.
36349          * @param {number} [options.maxWait]
36350          *  The maximum time `func` is allowed to be delayed before it's invoked.
36351          * @param {boolean} [options.trailing=true]
36352          *  Specify invoking on the trailing edge of the timeout.
36353          * @returns {Function} Returns the new debounced function.
36354          * @example
36355          *
36356          * // Avoid costly calculations while the window size is in flux.
36357          * jQuery(window).on('resize', _.debounce(calculateLayout, 150));
36358          *
36359          * // Invoke `sendMail` when clicked, debouncing subsequent calls.
36360          * jQuery(element).on('click', _.debounce(sendMail, 300, {
36361          *   'leading': true,
36362          *   'trailing': false
36363          * }));
36364          *
36365          * // Ensure `batchLog` is invoked once after 1 second of debounced calls.
36366          * var debounced = _.debounce(batchLog, 250, { 'maxWait': 1000 });
36367          * var source = new EventSource('/stream');
36368          * jQuery(source).on('message', debounced);
36369          *
36370          * // Cancel the trailing debounced invocation.
36371          * jQuery(window).on('popstate', debounced.cancel);
36372          */
36373         function debounce(func, wait, options) {
36374           var lastArgs,
36375               lastThis,
36376               maxWait,
36377               result,
36378               timerId,
36379               lastCallTime,
36380               lastInvokeTime = 0,
36381               leading = false,
36382               maxing = false,
36383               trailing = true;
36384
36385           if (typeof func != 'function') {
36386             throw new TypeError(FUNC_ERROR_TEXT);
36387           }
36388           wait = toNumber(wait) || 0;
36389           if (isObject$1(options)) {
36390             leading = !!options.leading;
36391             maxing = 'maxWait' in options;
36392             maxWait = maxing ? nativeMax(toNumber(options.maxWait) || 0, wait) : maxWait;
36393             trailing = 'trailing' in options ? !!options.trailing : trailing;
36394           }
36395
36396           function invokeFunc(time) {
36397             var args = lastArgs,
36398                 thisArg = lastThis;
36399
36400             lastArgs = lastThis = undefined;
36401             lastInvokeTime = time;
36402             result = func.apply(thisArg, args);
36403             return result;
36404           }
36405
36406           function leadingEdge(time) {
36407             // Reset any `maxWait` timer.
36408             lastInvokeTime = time;
36409             // Start the timer for the trailing edge.
36410             timerId = setTimeout(timerExpired, wait);
36411             // Invoke the leading edge.
36412             return leading ? invokeFunc(time) : result;
36413           }
36414
36415           function remainingWait(time) {
36416             var timeSinceLastCall = time - lastCallTime,
36417                 timeSinceLastInvoke = time - lastInvokeTime,
36418                 timeWaiting = wait - timeSinceLastCall;
36419
36420             return maxing
36421               ? nativeMin(timeWaiting, maxWait - timeSinceLastInvoke)
36422               : timeWaiting;
36423           }
36424
36425           function shouldInvoke(time) {
36426             var timeSinceLastCall = time - lastCallTime,
36427                 timeSinceLastInvoke = time - lastInvokeTime;
36428
36429             // Either this is the first call, activity has stopped and we're at the
36430             // trailing edge, the system time has gone backwards and we're treating
36431             // it as the trailing edge, or we've hit the `maxWait` limit.
36432             return (lastCallTime === undefined || (timeSinceLastCall >= wait) ||
36433               (timeSinceLastCall < 0) || (maxing && timeSinceLastInvoke >= maxWait));
36434           }
36435
36436           function timerExpired() {
36437             var time = now$1();
36438             if (shouldInvoke(time)) {
36439               return trailingEdge(time);
36440             }
36441             // Restart the timer.
36442             timerId = setTimeout(timerExpired, remainingWait(time));
36443           }
36444
36445           function trailingEdge(time) {
36446             timerId = undefined;
36447
36448             // Only invoke if we have `lastArgs` which means `func` has been
36449             // debounced at least once.
36450             if (trailing && lastArgs) {
36451               return invokeFunc(time);
36452             }
36453             lastArgs = lastThis = undefined;
36454             return result;
36455           }
36456
36457           function cancel() {
36458             if (timerId !== undefined) {
36459               clearTimeout(timerId);
36460             }
36461             lastInvokeTime = 0;
36462             lastArgs = lastCallTime = lastThis = timerId = undefined;
36463           }
36464
36465           function flush() {
36466             return timerId === undefined ? result : trailingEdge(now$1());
36467           }
36468
36469           function debounced() {
36470             var time = now$1(),
36471                 isInvoking = shouldInvoke(time);
36472
36473             lastArgs = arguments;
36474             lastThis = this;
36475             lastCallTime = time;
36476
36477             if (isInvoking) {
36478               if (timerId === undefined) {
36479                 return leadingEdge(lastCallTime);
36480               }
36481               if (maxing) {
36482                 // Handle invocations in a tight loop.
36483                 clearTimeout(timerId);
36484                 timerId = setTimeout(timerExpired, wait);
36485                 return invokeFunc(lastCallTime);
36486               }
36487             }
36488             if (timerId === undefined) {
36489               timerId = setTimeout(timerExpired, wait);
36490             }
36491             return result;
36492           }
36493           debounced.cancel = cancel;
36494           debounced.flush = flush;
36495           return debounced;
36496         }
36497
36498         /** Error message constants. */
36499         var FUNC_ERROR_TEXT$1 = 'Expected a function';
36500
36501         /**
36502          * Creates a throttled function that only invokes `func` at most once per
36503          * every `wait` milliseconds. The throttled function comes with a `cancel`
36504          * method to cancel delayed `func` invocations and a `flush` method to
36505          * immediately invoke them. Provide `options` to indicate whether `func`
36506          * should be invoked on the leading and/or trailing edge of the `wait`
36507          * timeout. The `func` is invoked with the last arguments provided to the
36508          * throttled function. Subsequent calls to the throttled function return the
36509          * result of the last `func` invocation.
36510          *
36511          * **Note:** If `leading` and `trailing` options are `true`, `func` is
36512          * invoked on the trailing edge of the timeout only if the throttled function
36513          * is invoked more than once during the `wait` timeout.
36514          *
36515          * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred
36516          * until to the next tick, similar to `setTimeout` with a timeout of `0`.
36517          *
36518          * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)
36519          * for details over the differences between `_.throttle` and `_.debounce`.
36520          *
36521          * @static
36522          * @memberOf _
36523          * @since 0.1.0
36524          * @category Function
36525          * @param {Function} func The function to throttle.
36526          * @param {number} [wait=0] The number of milliseconds to throttle invocations to.
36527          * @param {Object} [options={}] The options object.
36528          * @param {boolean} [options.leading=true]
36529          *  Specify invoking on the leading edge of the timeout.
36530          * @param {boolean} [options.trailing=true]
36531          *  Specify invoking on the trailing edge of the timeout.
36532          * @returns {Function} Returns the new throttled function.
36533          * @example
36534          *
36535          * // Avoid excessively updating the position while scrolling.
36536          * jQuery(window).on('scroll', _.throttle(updatePosition, 100));
36537          *
36538          * // Invoke `renewToken` when the click event is fired, but not more than once every 5 minutes.
36539          * var throttled = _.throttle(renewToken, 300000, { 'trailing': false });
36540          * jQuery(element).on('click', throttled);
36541          *
36542          * // Cancel the trailing throttled invocation.
36543          * jQuery(window).on('popstate', throttled.cancel);
36544          */
36545         function throttle(func, wait, options) {
36546           var leading = true,
36547               trailing = true;
36548
36549           if (typeof func != 'function') {
36550             throw new TypeError(FUNC_ERROR_TEXT$1);
36551           }
36552           if (isObject$1(options)) {
36553             leading = 'leading' in options ? !!options.leading : leading;
36554             trailing = 'trailing' in options ? !!options.trailing : trailing;
36555           }
36556           return debounce(func, wait, {
36557             'leading': leading,
36558             'maxWait': wait,
36559             'trailing': trailing
36560           });
36561         }
36562
36563         var hashes = createCommonjsModule(function (module, exports) {
36564         /**
36565          * jshashes - https://github.com/h2non/jshashes
36566          * Released under the "New BSD" license
36567          *
36568          * Algorithms specification:
36569          *
36570          * MD5 - http://www.ietf.org/rfc/rfc1321.txt
36571          * RIPEMD-160 - http://homes.esat.kuleuven.be/~bosselae/ripemd160.html
36572          * SHA1   - http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf
36573          * SHA256 - http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf
36574          * SHA512 - http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf
36575          * HMAC - http://www.ietf.org/rfc/rfc2104.txt
36576          */
36577         (function() {
36578           var Hashes;
36579
36580           function utf8Encode(str) {
36581             var x, y, output = '',
36582               i = -1,
36583               l;
36584
36585             if (str && str.length) {
36586               l = str.length;
36587               while ((i += 1) < l) {
36588                 /* Decode utf-16 surrogate pairs */
36589                 x = str.charCodeAt(i);
36590                 y = i + 1 < l ? str.charCodeAt(i + 1) : 0;
36591                 if (0xD800 <= x && x <= 0xDBFF && 0xDC00 <= y && y <= 0xDFFF) {
36592                   x = 0x10000 + ((x & 0x03FF) << 10) + (y & 0x03FF);
36593                   i += 1;
36594                 }
36595                 /* Encode output as utf-8 */
36596                 if (x <= 0x7F) {
36597                   output += String.fromCharCode(x);
36598                 } else if (x <= 0x7FF) {
36599                   output += String.fromCharCode(0xC0 | ((x >>> 6) & 0x1F),
36600                     0x80 | (x & 0x3F));
36601                 } else if (x <= 0xFFFF) {
36602                   output += String.fromCharCode(0xE0 | ((x >>> 12) & 0x0F),
36603                     0x80 | ((x >>> 6) & 0x3F),
36604                     0x80 | (x & 0x3F));
36605                 } else if (x <= 0x1FFFFF) {
36606                   output += String.fromCharCode(0xF0 | ((x >>> 18) & 0x07),
36607                     0x80 | ((x >>> 12) & 0x3F),
36608                     0x80 | ((x >>> 6) & 0x3F),
36609                     0x80 | (x & 0x3F));
36610                 }
36611               }
36612             }
36613             return output;
36614           }
36615
36616           function utf8Decode(str) {
36617             var i, ac, c1, c2, c3, arr = [],
36618               l;
36619             i = ac = c1 = c2 = c3 = 0;
36620
36621             if (str && str.length) {
36622               l = str.length;
36623               str += '';
36624
36625               while (i < l) {
36626                 c1 = str.charCodeAt(i);
36627                 ac += 1;
36628                 if (c1 < 128) {
36629                   arr[ac] = String.fromCharCode(c1);
36630                   i += 1;
36631                 } else if (c1 > 191 && c1 < 224) {
36632                   c2 = str.charCodeAt(i + 1);
36633                   arr[ac] = String.fromCharCode(((c1 & 31) << 6) | (c2 & 63));
36634                   i += 2;
36635                 } else {
36636                   c2 = str.charCodeAt(i + 1);
36637                   c3 = str.charCodeAt(i + 2);
36638                   arr[ac] = String.fromCharCode(((c1 & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
36639                   i += 3;
36640                 }
36641               }
36642             }
36643             return arr.join('');
36644           }
36645
36646           /**
36647            * Add integers, wrapping at 2^32. This uses 16-bit operations internally
36648            * to work around bugs in some JS interpreters.
36649            */
36650
36651           function safe_add(x, y) {
36652             var lsw = (x & 0xFFFF) + (y & 0xFFFF),
36653               msw = (x >> 16) + (y >> 16) + (lsw >> 16);
36654             return (msw << 16) | (lsw & 0xFFFF);
36655           }
36656
36657           /**
36658            * Bitwise rotate a 32-bit number to the left.
36659            */
36660
36661           function bit_rol(num, cnt) {
36662             return (num << cnt) | (num >>> (32 - cnt));
36663           }
36664
36665           /**
36666            * Convert a raw string to a hex string
36667            */
36668
36669           function rstr2hex(input, hexcase) {
36670             var hex_tab = hexcase ? '0123456789ABCDEF' : '0123456789abcdef',
36671               output = '',
36672               x, i = 0,
36673               l = input.length;
36674             for (; i < l; i += 1) {
36675               x = input.charCodeAt(i);
36676               output += hex_tab.charAt((x >>> 4) & 0x0F) + hex_tab.charAt(x & 0x0F);
36677             }
36678             return output;
36679           }
36680
36681           /**
36682            * Convert an array of big-endian words to a string
36683            */
36684
36685           function binb2rstr(input) {
36686             var i, l = input.length * 32,
36687               output = '';
36688             for (i = 0; i < l; i += 8) {
36689               output += String.fromCharCode((input[i >> 5] >>> (24 - i % 32)) & 0xFF);
36690             }
36691             return output;
36692           }
36693
36694           /**
36695            * Convert an array of little-endian words to a string
36696            */
36697
36698           function binl2rstr(input) {
36699             var i, l = input.length * 32,
36700               output = '';
36701             for (i = 0; i < l; i += 8) {
36702               output += String.fromCharCode((input[i >> 5] >>> (i % 32)) & 0xFF);
36703             }
36704             return output;
36705           }
36706
36707           /**
36708            * Convert a raw string to an array of little-endian words
36709            * Characters >255 have their high-byte silently ignored.
36710            */
36711
36712           function rstr2binl(input) {
36713             var i, l = input.length * 8,
36714               output = Array(input.length >> 2),
36715               lo = output.length;
36716             for (i = 0; i < lo; i += 1) {
36717               output[i] = 0;
36718             }
36719             for (i = 0; i < l; i += 8) {
36720               output[i >> 5] |= (input.charCodeAt(i / 8) & 0xFF) << (i % 32);
36721             }
36722             return output;
36723           }
36724
36725           /**
36726            * Convert a raw string to an array of big-endian words
36727            * Characters >255 have their high-byte silently ignored.
36728            */
36729
36730           function rstr2binb(input) {
36731             var i, l = input.length * 8,
36732               output = Array(input.length >> 2),
36733               lo = output.length;
36734             for (i = 0; i < lo; i += 1) {
36735               output[i] = 0;
36736             }
36737             for (i = 0; i < l; i += 8) {
36738               output[i >> 5] |= (input.charCodeAt(i / 8) & 0xFF) << (24 - i % 32);
36739             }
36740             return output;
36741           }
36742
36743           /**
36744            * Convert a raw string to an arbitrary string encoding
36745            */
36746
36747           function rstr2any(input, encoding) {
36748             var divisor = encoding.length,
36749               remainders = Array(),
36750               i, q, x, ld, quotient, dividend, output, full_length;
36751
36752             /* Convert to an array of 16-bit big-endian values, forming the dividend */
36753             dividend = Array(Math.ceil(input.length / 2));
36754             ld = dividend.length;
36755             for (i = 0; i < ld; i += 1) {
36756               dividend[i] = (input.charCodeAt(i * 2) << 8) | input.charCodeAt(i * 2 + 1);
36757             }
36758
36759             /**
36760              * Repeatedly perform a long division. The binary array forms the dividend,
36761              * the length of the encoding is the divisor. Once computed, the quotient
36762              * forms the dividend for the next step. We stop when the dividend is zerHashes.
36763              * All remainders are stored for later use.
36764              */
36765             while (dividend.length > 0) {
36766               quotient = Array();
36767               x = 0;
36768               for (i = 0; i < dividend.length; i += 1) {
36769                 x = (x << 16) + dividend[i];
36770                 q = Math.floor(x / divisor);
36771                 x -= q * divisor;
36772                 if (quotient.length > 0 || q > 0) {
36773                   quotient[quotient.length] = q;
36774                 }
36775               }
36776               remainders[remainders.length] = x;
36777               dividend = quotient;
36778             }
36779
36780             /* Convert the remainders to the output string */
36781             output = '';
36782             for (i = remainders.length - 1; i >= 0; i--) {
36783               output += encoding.charAt(remainders[i]);
36784             }
36785
36786             /* Append leading zero equivalents */
36787             full_length = Math.ceil(input.length * 8 / (Math.log(encoding.length) / Math.log(2)));
36788             for (i = output.length; i < full_length; i += 1) {
36789               output = encoding[0] + output;
36790             }
36791             return output;
36792           }
36793
36794           /**
36795            * Convert a raw string to a base-64 string
36796            */
36797
36798           function rstr2b64(input, b64pad) {
36799             var tab = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',
36800               output = '',
36801               len = input.length,
36802               i, j, triplet;
36803             b64pad = b64pad || '=';
36804             for (i = 0; i < len; i += 3) {
36805               triplet = (input.charCodeAt(i) << 16) | (i + 1 < len ? input.charCodeAt(i + 1) << 8 : 0) | (i + 2 < len ? input.charCodeAt(i + 2) : 0);
36806               for (j = 0; j < 4; j += 1) {
36807                 if (i * 8 + j * 6 > input.length * 8) {
36808                   output += b64pad;
36809                 } else {
36810                   output += tab.charAt((triplet >>> 6 * (3 - j)) & 0x3F);
36811                 }
36812               }
36813             }
36814             return output;
36815           }
36816
36817           Hashes = {
36818             /**
36819              * @property {String} version
36820              * @readonly
36821              */
36822             VERSION: '1.0.6',
36823             /**
36824              * @member Hashes
36825              * @class Base64
36826              * @constructor
36827              */
36828             Base64: function() {
36829               // private properties
36830               var tab = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',
36831                 pad = '=', // default pad according with the RFC standard
36832                 utf8 = true; // by default enable UTF-8 support encoding
36833
36834               // public method for encoding
36835               this.encode = function(input) {
36836                 var i, j, triplet,
36837                   output = '',
36838                   len = input.length;
36839
36840                 pad = pad || '=';
36841                 input = (utf8) ? utf8Encode(input) : input;
36842
36843                 for (i = 0; i < len; i += 3) {
36844                   triplet = (input.charCodeAt(i) << 16) | (i + 1 < len ? input.charCodeAt(i + 1) << 8 : 0) | (i + 2 < len ? input.charCodeAt(i + 2) : 0);
36845                   for (j = 0; j < 4; j += 1) {
36846                     if (i * 8 + j * 6 > len * 8) {
36847                       output += pad;
36848                     } else {
36849                       output += tab.charAt((triplet >>> 6 * (3 - j)) & 0x3F);
36850                     }
36851                   }
36852                 }
36853                 return output;
36854               };
36855
36856               // public method for decoding
36857               this.decode = function(input) {
36858                 // var b64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
36859                 var i, o1, o2, o3, h1, h2, h3, h4, bits, ac,
36860                   dec = '',
36861                   arr = [];
36862                 if (!input) {
36863                   return input;
36864                 }
36865
36866                 i = ac = 0;
36867                 input = input.replace(new RegExp('\\' + pad, 'gi'), ''); // use '='
36868                 //input += '';
36869
36870                 do { // unpack four hexets into three octets using index points in b64
36871                   h1 = tab.indexOf(input.charAt(i += 1));
36872                   h2 = tab.indexOf(input.charAt(i += 1));
36873                   h3 = tab.indexOf(input.charAt(i += 1));
36874                   h4 = tab.indexOf(input.charAt(i += 1));
36875
36876                   bits = h1 << 18 | h2 << 12 | h3 << 6 | h4;
36877
36878                   o1 = bits >> 16 & 0xff;
36879                   o2 = bits >> 8 & 0xff;
36880                   o3 = bits & 0xff;
36881                   ac += 1;
36882
36883                   if (h3 === 64) {
36884                     arr[ac] = String.fromCharCode(o1);
36885                   } else if (h4 === 64) {
36886                     arr[ac] = String.fromCharCode(o1, o2);
36887                   } else {
36888                     arr[ac] = String.fromCharCode(o1, o2, o3);
36889                   }
36890                 } while (i < input.length);
36891
36892                 dec = arr.join('');
36893                 dec = (utf8) ? utf8Decode(dec) : dec;
36894
36895                 return dec;
36896               };
36897
36898               // set custom pad string
36899               this.setPad = function(str) {
36900                 pad = str || pad;
36901                 return this;
36902               };
36903               // set custom tab string characters
36904               this.setTab = function(str) {
36905                 tab = str || tab;
36906                 return this;
36907               };
36908               this.setUTF8 = function(bool) {
36909                 if (typeof bool === 'boolean') {
36910                   utf8 = bool;
36911                 }
36912                 return this;
36913               };
36914             },
36915
36916             /**
36917              * CRC-32 calculation
36918              * @member Hashes
36919              * @method CRC32
36920              * @static
36921              * @param {String} str Input String
36922              * @return {String}
36923              */
36924             CRC32: function(str) {
36925               var crc = 0,
36926                 x = 0,
36927                 y = 0,
36928                 table, i, iTop;
36929               str = utf8Encode(str);
36930
36931               table = [
36932                 '00000000 77073096 EE0E612C 990951BA 076DC419 706AF48F E963A535 9E6495A3 0EDB8832 ',
36933                 '79DCB8A4 E0D5E91E 97D2D988 09B64C2B 7EB17CBD E7B82D07 90BF1D91 1DB71064 6AB020F2 F3B97148 ',
36934                 '84BE41DE 1ADAD47D 6DDDE4EB F4D4B551 83D385C7 136C9856 646BA8C0 FD62F97A 8A65C9EC 14015C4F ',
36935                 '63066CD9 FA0F3D63 8D080DF5 3B6E20C8 4C69105E D56041E4 A2677172 3C03E4D1 4B04D447 D20D85FD ',
36936                 'A50AB56B 35B5A8FA 42B2986C DBBBC9D6 ACBCF940 32D86CE3 45DF5C75 DCD60DCF ABD13D59 26D930AC ',
36937                 '51DE003A C8D75180 BFD06116 21B4F4B5 56B3C423 CFBA9599 B8BDA50F 2802B89E 5F058808 C60CD9B2 ',
36938                 'B10BE924 2F6F7C87 58684C11 C1611DAB B6662D3D 76DC4190 01DB7106 98D220BC EFD5102A 71B18589 ',
36939                 '06B6B51F 9FBFE4A5 E8B8D433 7807C9A2 0F00F934 9609A88E E10E9818 7F6A0DBB 086D3D2D 91646C97 ',
36940                 'E6635C01 6B6B51F4 1C6C6162 856530D8 F262004E 6C0695ED 1B01A57B 8208F4C1 F50FC457 65B0D9C6 ',
36941                 '12B7E950 8BBEB8EA FCB9887C 62DD1DDF 15DA2D49 8CD37CF3 FBD44C65 4DB26158 3AB551CE A3BC0074 ',
36942                 'D4BB30E2 4ADFA541 3DD895D7 A4D1C46D D3D6F4FB 4369E96A 346ED9FC AD678846 DA60B8D0 44042D73 ',
36943                 '33031DE5 AA0A4C5F DD0D7CC9 5005713C 270241AA BE0B1010 C90C2086 5768B525 206F85B3 B966D409 ',
36944                 'CE61E49F 5EDEF90E 29D9C998 B0D09822 C7D7A8B4 59B33D17 2EB40D81 B7BD5C3B C0BA6CAD EDB88320 ',
36945                 '9ABFB3B6 03B6E20C 74B1D29A EAD54739 9DD277AF 04DB2615 73DC1683 E3630B12 94643B84 0D6D6A3E ',
36946                 '7A6A5AA8 E40ECF0B 9309FF9D 0A00AE27 7D079EB1 F00F9344 8708A3D2 1E01F268 6906C2FE F762575D ',
36947                 '806567CB 196C3671 6E6B06E7 FED41B76 89D32BE0 10DA7A5A 67DD4ACC F9B9DF6F 8EBEEFF9 17B7BE43 ',
36948                 '60B08ED5 D6D6A3E8 A1D1937E 38D8C2C4 4FDFF252 D1BB67F1 A6BC5767 3FB506DD 48B2364B D80D2BDA ',
36949                 'AF0A1B4C 36034AF6 41047A60 DF60EFC3 A867DF55 316E8EEF 4669BE79 CB61B38C BC66831A 256FD2A0 ',
36950                 '5268E236 CC0C7795 BB0B4703 220216B9 5505262F C5BA3BBE B2BD0B28 2BB45A92 5CB36A04 C2D7FFA7 ',
36951                 'B5D0CF31 2CD99E8B 5BDEAE1D 9B64C2B0 EC63F226 756AA39C 026D930A 9C0906A9 EB0E363F 72076785 ',
36952                 '05005713 95BF4A82 E2B87A14 7BB12BAE 0CB61B38 92D28E9B E5D5BE0D 7CDCEFB7 0BDBDF21 86D3D2D4 ',
36953                 'F1D4E242 68DDB3F8 1FDA836E 81BE16CD F6B9265B 6FB077E1 18B74777 88085AE6 FF0F6A70 66063BCA ',
36954                 '11010B5C 8F659EFF F862AE69 616BFFD3 166CCF45 A00AE278 D70DD2EE 4E048354 3903B3C2 A7672661 ',
36955                 'D06016F7 4969474D 3E6E77DB AED16A4A D9D65ADC 40DF0B66 37D83BF0 A9BCAE53 DEBB9EC5 47B2CF7F ',
36956                 '30B5FFE9 BDBDF21C CABAC28A 53B39330 24B4A3A6 BAD03605 CDD70693 54DE5729 23D967BF B3667A2E ',
36957                 'C4614AB8 5D681B02 2A6F2B94 B40BBE37 C30C8EA1 5A05DF1B 2D02EF8D'
36958               ].join('');
36959
36960               crc = crc ^ (-1);
36961               for (i = 0, iTop = str.length; i < iTop; i += 1) {
36962                 y = (crc ^ str.charCodeAt(i)) & 0xFF;
36963                 x = '0x' + table.substr(y * 9, 8);
36964                 crc = (crc >>> 8) ^ x;
36965               }
36966               // always return a positive number (that's what >>> 0 does)
36967               return (crc ^ (-1)) >>> 0;
36968             },
36969             /**
36970              * @member Hashes
36971              * @class MD5
36972              * @constructor
36973              * @param {Object} [config]
36974              *
36975              * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
36976              * Digest Algorithm, as defined in RFC 1321.
36977              * Version 2.2 Copyright (C) Paul Johnston 1999 - 2009
36978              * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
36979              * See <http://pajhome.org.uk/crypt/md5> for more infHashes.
36980              */
36981             MD5: function(options) {
36982               /**
36983                * Private config properties. You may need to tweak these to be compatible with
36984                * the server-side, but the defaults work in most cases.
36985                * See {@link Hashes.MD5#method-setUpperCase} and {@link Hashes.SHA1#method-setUpperCase}
36986                */
36987               var hexcase = (options && typeof options.uppercase === 'boolean') ? options.uppercase : false, // hexadecimal output case format. false - lowercase; true - uppercase
36988                 b64pad = (options && typeof options.pad === 'string') ? options.pad : '=', // base-64 pad character. Defaults to '=' for strict RFC compliance
36989                 utf8 = (options && typeof options.utf8 === 'boolean') ? options.utf8 : true; // enable/disable utf8 encoding
36990
36991               // privileged (public) methods
36992               this.hex = function(s) {
36993                 return rstr2hex(rstr(s), hexcase);
36994               };
36995               this.b64 = function(s) {
36996                 return rstr2b64(rstr(s), b64pad);
36997               };
36998               this.any = function(s, e) {
36999                 return rstr2any(rstr(s), e);
37000               };
37001               this.raw = function(s) {
37002                 return rstr(s);
37003               };
37004               this.hex_hmac = function(k, d) {
37005                 return rstr2hex(rstr_hmac(k, d), hexcase);
37006               };
37007               this.b64_hmac = function(k, d) {
37008                 return rstr2b64(rstr_hmac(k, d), b64pad);
37009               };
37010               this.any_hmac = function(k, d, e) {
37011                 return rstr2any(rstr_hmac(k, d), e);
37012               };
37013               /**
37014                * Perform a simple self-test to see if the VM is working
37015                * @return {String} Hexadecimal hash sample
37016                */
37017               this.vm_test = function() {
37018                 return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72';
37019               };
37020               /**
37021                * Enable/disable uppercase hexadecimal returned string
37022                * @param {Boolean}
37023                * @return {Object} this
37024                */
37025               this.setUpperCase = function(a) {
37026                 if (typeof a === 'boolean') {
37027                   hexcase = a;
37028                 }
37029                 return this;
37030               };
37031               /**
37032                * Defines a base64 pad string
37033                * @param {String} Pad
37034                * @return {Object} this
37035                */
37036               this.setPad = function(a) {
37037                 b64pad = a || b64pad;
37038                 return this;
37039               };
37040               /**
37041                * Defines a base64 pad string
37042                * @param {Boolean}
37043                * @return {Object} [this]
37044                */
37045               this.setUTF8 = function(a) {
37046                 if (typeof a === 'boolean') {
37047                   utf8 = a;
37048                 }
37049                 return this;
37050               };
37051
37052               // private methods
37053
37054               /**
37055                * Calculate the MD5 of a raw string
37056                */
37057
37058               function rstr(s) {
37059                 s = (utf8) ? utf8Encode(s) : s;
37060                 return binl2rstr(binl(rstr2binl(s), s.length * 8));
37061               }
37062
37063               /**
37064                * Calculate the HMAC-MD5, of a key and some data (raw strings)
37065                */
37066
37067               function rstr_hmac(key, data) {
37068                 var bkey, ipad, opad, hash, i;
37069
37070                 key = (utf8) ? utf8Encode(key) : key;
37071                 data = (utf8) ? utf8Encode(data) : data;
37072                 bkey = rstr2binl(key);
37073                 if (bkey.length > 16) {
37074                   bkey = binl(bkey, key.length * 8);
37075                 }
37076
37077                 ipad = Array(16), opad = Array(16);
37078                 for (i = 0; i < 16; i += 1) {
37079                   ipad[i] = bkey[i] ^ 0x36363636;
37080                   opad[i] = bkey[i] ^ 0x5C5C5C5C;
37081                 }
37082                 hash = binl(ipad.concat(rstr2binl(data)), 512 + data.length * 8);
37083                 return binl2rstr(binl(opad.concat(hash), 512 + 128));
37084               }
37085
37086               /**
37087                * Calculate the MD5 of an array of little-endian words, and a bit length.
37088                */
37089
37090               function binl(x, len) {
37091                 var i, olda, oldb, oldc, oldd,
37092                   a = 1732584193,
37093                   b = -271733879,
37094                   c = -1732584194,
37095                   d = 271733878;
37096
37097                 /* append padding */
37098                 x[len >> 5] |= 0x80 << ((len) % 32);
37099                 x[(((len + 64) >>> 9) << 4) + 14] = len;
37100
37101                 for (i = 0; i < x.length; i += 16) {
37102                   olda = a;
37103                   oldb = b;
37104                   oldc = c;
37105                   oldd = d;
37106
37107                   a = md5_ff(a, b, c, d, x[i + 0], 7, -680876936);
37108                   d = md5_ff(d, a, b, c, x[i + 1], 12, -389564586);
37109                   c = md5_ff(c, d, a, b, x[i + 2], 17, 606105819);
37110                   b = md5_ff(b, c, d, a, x[i + 3], 22, -1044525330);
37111                   a = md5_ff(a, b, c, d, x[i + 4], 7, -176418897);
37112                   d = md5_ff(d, a, b, c, x[i + 5], 12, 1200080426);
37113                   c = md5_ff(c, d, a, b, x[i + 6], 17, -1473231341);
37114                   b = md5_ff(b, c, d, a, x[i + 7], 22, -45705983);
37115                   a = md5_ff(a, b, c, d, x[i + 8], 7, 1770035416);
37116                   d = md5_ff(d, a, b, c, x[i + 9], 12, -1958414417);
37117                   c = md5_ff(c, d, a, b, x[i + 10], 17, -42063);
37118                   b = md5_ff(b, c, d, a, x[i + 11], 22, -1990404162);
37119                   a = md5_ff(a, b, c, d, x[i + 12], 7, 1804603682);
37120                   d = md5_ff(d, a, b, c, x[i + 13], 12, -40341101);
37121                   c = md5_ff(c, d, a, b, x[i + 14], 17, -1502002290);
37122                   b = md5_ff(b, c, d, a, x[i + 15], 22, 1236535329);
37123
37124                   a = md5_gg(a, b, c, d, x[i + 1], 5, -165796510);
37125                   d = md5_gg(d, a, b, c, x[i + 6], 9, -1069501632);
37126                   c = md5_gg(c, d, a, b, x[i + 11], 14, 643717713);
37127                   b = md5_gg(b, c, d, a, x[i + 0], 20, -373897302);
37128                   a = md5_gg(a, b, c, d, x[i + 5], 5, -701558691);
37129                   d = md5_gg(d, a, b, c, x[i + 10], 9, 38016083);
37130                   c = md5_gg(c, d, a, b, x[i + 15], 14, -660478335);
37131                   b = md5_gg(b, c, d, a, x[i + 4], 20, -405537848);
37132                   a = md5_gg(a, b, c, d, x[i + 9], 5, 568446438);
37133                   d = md5_gg(d, a, b, c, x[i + 14], 9, -1019803690);
37134                   c = md5_gg(c, d, a, b, x[i + 3], 14, -187363961);
37135                   b = md5_gg(b, c, d, a, x[i + 8], 20, 1163531501);
37136                   a = md5_gg(a, b, c, d, x[i + 13], 5, -1444681467);
37137                   d = md5_gg(d, a, b, c, x[i + 2], 9, -51403784);
37138                   c = md5_gg(c, d, a, b, x[i + 7], 14, 1735328473);
37139                   b = md5_gg(b, c, d, a, x[i + 12], 20, -1926607734);
37140
37141                   a = md5_hh(a, b, c, d, x[i + 5], 4, -378558);
37142                   d = md5_hh(d, a, b, c, x[i + 8], 11, -2022574463);
37143                   c = md5_hh(c, d, a, b, x[i + 11], 16, 1839030562);
37144                   b = md5_hh(b, c, d, a, x[i + 14], 23, -35309556);
37145                   a = md5_hh(a, b, c, d, x[i + 1], 4, -1530992060);
37146                   d = md5_hh(d, a, b, c, x[i + 4], 11, 1272893353);
37147                   c = md5_hh(c, d, a, b, x[i + 7], 16, -155497632);
37148                   b = md5_hh(b, c, d, a, x[i + 10], 23, -1094730640);
37149                   a = md5_hh(a, b, c, d, x[i + 13], 4, 681279174);
37150                   d = md5_hh(d, a, b, c, x[i + 0], 11, -358537222);
37151                   c = md5_hh(c, d, a, b, x[i + 3], 16, -722521979);
37152                   b = md5_hh(b, c, d, a, x[i + 6], 23, 76029189);
37153                   a = md5_hh(a, b, c, d, x[i + 9], 4, -640364487);
37154                   d = md5_hh(d, a, b, c, x[i + 12], 11, -421815835);
37155                   c = md5_hh(c, d, a, b, x[i + 15], 16, 530742520);
37156                   b = md5_hh(b, c, d, a, x[i + 2], 23, -995338651);
37157
37158                   a = md5_ii(a, b, c, d, x[i + 0], 6, -198630844);
37159                   d = md5_ii(d, a, b, c, x[i + 7], 10, 1126891415);
37160                   c = md5_ii(c, d, a, b, x[i + 14], 15, -1416354905);
37161                   b = md5_ii(b, c, d, a, x[i + 5], 21, -57434055);
37162                   a = md5_ii(a, b, c, d, x[i + 12], 6, 1700485571);
37163                   d = md5_ii(d, a, b, c, x[i + 3], 10, -1894986606);
37164                   c = md5_ii(c, d, a, b, x[i + 10], 15, -1051523);
37165                   b = md5_ii(b, c, d, a, x[i + 1], 21, -2054922799);
37166                   a = md5_ii(a, b, c, d, x[i + 8], 6, 1873313359);
37167                   d = md5_ii(d, a, b, c, x[i + 15], 10, -30611744);
37168                   c = md5_ii(c, d, a, b, x[i + 6], 15, -1560198380);
37169                   b = md5_ii(b, c, d, a, x[i + 13], 21, 1309151649);
37170                   a = md5_ii(a, b, c, d, x[i + 4], 6, -145523070);
37171                   d = md5_ii(d, a, b, c, x[i + 11], 10, -1120210379);
37172                   c = md5_ii(c, d, a, b, x[i + 2], 15, 718787259);
37173                   b = md5_ii(b, c, d, a, x[i + 9], 21, -343485551);
37174
37175                   a = safe_add(a, olda);
37176                   b = safe_add(b, oldb);
37177                   c = safe_add(c, oldc);
37178                   d = safe_add(d, oldd);
37179                 }
37180                 return Array(a, b, c, d);
37181               }
37182
37183               /**
37184                * These functions implement the four basic operations the algorithm uses.
37185                */
37186
37187               function md5_cmn(q, a, b, x, s, t) {
37188                 return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s), b);
37189               }
37190
37191               function md5_ff(a, b, c, d, x, s, t) {
37192                 return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t);
37193               }
37194
37195               function md5_gg(a, b, c, d, x, s, t) {
37196                 return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t);
37197               }
37198
37199               function md5_hh(a, b, c, d, x, s, t) {
37200                 return md5_cmn(b ^ c ^ d, a, b, x, s, t);
37201               }
37202
37203               function md5_ii(a, b, c, d, x, s, t) {
37204                 return md5_cmn(c ^ (b | (~d)), a, b, x, s, t);
37205               }
37206             },
37207             /**
37208              * @member Hashes
37209              * @class Hashes.SHA1
37210              * @param {Object} [config]
37211              * @constructor
37212              *
37213              * A JavaScript implementation of the Secure Hash Algorithm, SHA-1, as defined in FIPS 180-1
37214              * Version 2.2 Copyright Paul Johnston 2000 - 2009.
37215              * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
37216              * See http://pajhome.org.uk/crypt/md5 for details.
37217              */
37218             SHA1: function(options) {
37219               /**
37220                * Private config properties. You may need to tweak these to be compatible with
37221                * the server-side, but the defaults work in most cases.
37222                * See {@link Hashes.MD5#method-setUpperCase} and {@link Hashes.SHA1#method-setUpperCase}
37223                */
37224               var hexcase = (options && typeof options.uppercase === 'boolean') ? options.uppercase : false, // hexadecimal output case format. false - lowercase; true - uppercase
37225                 b64pad = (options && typeof options.pad === 'string') ? options.pad : '=', // base-64 pad character. Defaults to '=' for strict RFC compliance
37226                 utf8 = (options && typeof options.utf8 === 'boolean') ? options.utf8 : true; // enable/disable utf8 encoding
37227
37228               // public methods
37229               this.hex = function(s) {
37230                 return rstr2hex(rstr(s), hexcase);
37231               };
37232               this.b64 = function(s) {
37233                 return rstr2b64(rstr(s), b64pad);
37234               };
37235               this.any = function(s, e) {
37236                 return rstr2any(rstr(s), e);
37237               };
37238               this.raw = function(s) {
37239                 return rstr(s);
37240               };
37241               this.hex_hmac = function(k, d) {
37242                 return rstr2hex(rstr_hmac(k, d));
37243               };
37244               this.b64_hmac = function(k, d) {
37245                 return rstr2b64(rstr_hmac(k, d), b64pad);
37246               };
37247               this.any_hmac = function(k, d, e) {
37248                 return rstr2any(rstr_hmac(k, d), e);
37249               };
37250               /**
37251                * Perform a simple self-test to see if the VM is working
37252                * @return {String} Hexadecimal hash sample
37253                * @public
37254                */
37255               this.vm_test = function() {
37256                 return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72';
37257               };
37258               /**
37259                * @description Enable/disable uppercase hexadecimal returned string
37260                * @param {boolean}
37261                * @return {Object} this
37262                * @public
37263                */
37264               this.setUpperCase = function(a) {
37265                 if (typeof a === 'boolean') {
37266                   hexcase = a;
37267                 }
37268                 return this;
37269               };
37270               /**
37271                * @description Defines a base64 pad string
37272                * @param {string} Pad
37273                * @return {Object} this
37274                * @public
37275                */
37276               this.setPad = function(a) {
37277                 b64pad = a || b64pad;
37278                 return this;
37279               };
37280               /**
37281                * @description Defines a base64 pad string
37282                * @param {boolean}
37283                * @return {Object} this
37284                * @public
37285                */
37286               this.setUTF8 = function(a) {
37287                 if (typeof a === 'boolean') {
37288                   utf8 = a;
37289                 }
37290                 return this;
37291               };
37292
37293               // private methods
37294
37295               /**
37296                * Calculate the SHA-512 of a raw string
37297                */
37298
37299               function rstr(s) {
37300                 s = (utf8) ? utf8Encode(s) : s;
37301                 return binb2rstr(binb(rstr2binb(s), s.length * 8));
37302               }
37303
37304               /**
37305                * Calculate the HMAC-SHA1 of a key and some data (raw strings)
37306                */
37307
37308               function rstr_hmac(key, data) {
37309                 var bkey, ipad, opad, i, hash;
37310                 key = (utf8) ? utf8Encode(key) : key;
37311                 data = (utf8) ? utf8Encode(data) : data;
37312                 bkey = rstr2binb(key);
37313
37314                 if (bkey.length > 16) {
37315                   bkey = binb(bkey, key.length * 8);
37316                 }
37317                 ipad = Array(16), opad = Array(16);
37318                 for (i = 0; i < 16; i += 1) {
37319                   ipad[i] = bkey[i] ^ 0x36363636;
37320                   opad[i] = bkey[i] ^ 0x5C5C5C5C;
37321                 }
37322                 hash = binb(ipad.concat(rstr2binb(data)), 512 + data.length * 8);
37323                 return binb2rstr(binb(opad.concat(hash), 512 + 160));
37324               }
37325
37326               /**
37327                * Calculate the SHA-1 of an array of big-endian words, and a bit length
37328                */
37329
37330               function binb(x, len) {
37331                 var i, j, t, olda, oldb, oldc, oldd, olde,
37332                   w = Array(80),
37333                   a = 1732584193,
37334                   b = -271733879,
37335                   c = -1732584194,
37336                   d = 271733878,
37337                   e = -1009589776;
37338
37339                 /* append padding */
37340                 x[len >> 5] |= 0x80 << (24 - len % 32);
37341                 x[((len + 64 >> 9) << 4) + 15] = len;
37342
37343                 for (i = 0; i < x.length; i += 16) {
37344                   olda = a;
37345                   oldb = b;
37346                   oldc = c;
37347                   oldd = d;
37348                   olde = e;
37349
37350                   for (j = 0; j < 80; j += 1) {
37351                     if (j < 16) {
37352                       w[j] = x[i + j];
37353                     } else {
37354                       w[j] = bit_rol(w[j - 3] ^ w[j - 8] ^ w[j - 14] ^ w[j - 16], 1);
37355                     }
37356                     t = safe_add(safe_add(bit_rol(a, 5), sha1_ft(j, b, c, d)),
37357                       safe_add(safe_add(e, w[j]), sha1_kt(j)));
37358                     e = d;
37359                     d = c;
37360                     c = bit_rol(b, 30);
37361                     b = a;
37362                     a = t;
37363                   }
37364
37365                   a = safe_add(a, olda);
37366                   b = safe_add(b, oldb);
37367                   c = safe_add(c, oldc);
37368                   d = safe_add(d, oldd);
37369                   e = safe_add(e, olde);
37370                 }
37371                 return Array(a, b, c, d, e);
37372               }
37373
37374               /**
37375                * Perform the appropriate triplet combination function for the current
37376                * iteration
37377                */
37378
37379               function sha1_ft(t, b, c, d) {
37380                 if (t < 20) {
37381                   return (b & c) | ((~b) & d);
37382                 }
37383                 if (t < 40) {
37384                   return b ^ c ^ d;
37385                 }
37386                 if (t < 60) {
37387                   return (b & c) | (b & d) | (c & d);
37388                 }
37389                 return b ^ c ^ d;
37390               }
37391
37392               /**
37393                * Determine the appropriate additive constant for the current iteration
37394                */
37395
37396               function sha1_kt(t) {
37397                 return (t < 20) ? 1518500249 : (t < 40) ? 1859775393 :
37398                   (t < 60) ? -1894007588 : -899497514;
37399               }
37400             },
37401             /**
37402              * @class Hashes.SHA256
37403              * @param {config}
37404              *
37405              * A JavaScript implementation of the Secure Hash Algorithm, SHA-256, as defined in FIPS 180-2
37406              * Version 2.2 Copyright Angel Marin, Paul Johnston 2000 - 2009.
37407              * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
37408              * See http://pajhome.org.uk/crypt/md5 for details.
37409              * Also http://anmar.eu.org/projects/jssha2/
37410              */
37411             SHA256: function(options) {
37412               /**
37413                * Private properties configuration variables. You may need to tweak these to be compatible with
37414                * the server-side, but the defaults work in most cases.
37415                * @see this.setUpperCase() method
37416                * @see this.setPad() method
37417                */
37418               var hexcase = (options && typeof options.uppercase === 'boolean') ? options.uppercase : false, // hexadecimal output case format. false - lowercase; true - uppercase  */
37419                 b64pad = (options && typeof options.pad === 'string') ? options.pad : '=',
37420                 /* base-64 pad character. Default '=' for strict RFC compliance   */
37421                 utf8 = (options && typeof options.utf8 === 'boolean') ? options.utf8 : true,
37422                 /* enable/disable utf8 encoding */
37423                 sha256_K;
37424
37425               /* privileged (public) methods */
37426               this.hex = function(s) {
37427                 return rstr2hex(rstr(s, utf8));
37428               };
37429               this.b64 = function(s) {
37430                 return rstr2b64(rstr(s, utf8), b64pad);
37431               };
37432               this.any = function(s, e) {
37433                 return rstr2any(rstr(s, utf8), e);
37434               };
37435               this.raw = function(s) {
37436                 return rstr(s, utf8);
37437               };
37438               this.hex_hmac = function(k, d) {
37439                 return rstr2hex(rstr_hmac(k, d));
37440               };
37441               this.b64_hmac = function(k, d) {
37442                 return rstr2b64(rstr_hmac(k, d), b64pad);
37443               };
37444               this.any_hmac = function(k, d, e) {
37445                 return rstr2any(rstr_hmac(k, d), e);
37446               };
37447               /**
37448                * Perform a simple self-test to see if the VM is working
37449                * @return {String} Hexadecimal hash sample
37450                * @public
37451                */
37452               this.vm_test = function() {
37453                 return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72';
37454               };
37455               /**
37456                * Enable/disable uppercase hexadecimal returned string
37457                * @param {boolean}
37458                * @return {Object} this
37459                * @public
37460                */
37461               this.setUpperCase = function(a) {
37462                 if (typeof a === 'boolean') {
37463                   hexcase = a;
37464                 }
37465                 return this;
37466               };
37467               /**
37468                * @description Defines a base64 pad string
37469                * @param {string} Pad
37470                * @return {Object} this
37471                * @public
37472                */
37473               this.setPad = function(a) {
37474                 b64pad = a || b64pad;
37475                 return this;
37476               };
37477               /**
37478                * Defines a base64 pad string
37479                * @param {boolean}
37480                * @return {Object} this
37481                * @public
37482                */
37483               this.setUTF8 = function(a) {
37484                 if (typeof a === 'boolean') {
37485                   utf8 = a;
37486                 }
37487                 return this;
37488               };
37489
37490               // private methods
37491
37492               /**
37493                * Calculate the SHA-512 of a raw string
37494                */
37495
37496               function rstr(s, utf8) {
37497                 s = (utf8) ? utf8Encode(s) : s;
37498                 return binb2rstr(binb(rstr2binb(s), s.length * 8));
37499               }
37500
37501               /**
37502                * Calculate the HMAC-sha256 of a key and some data (raw strings)
37503                */
37504
37505               function rstr_hmac(key, data) {
37506                 key = (utf8) ? utf8Encode(key) : key;
37507                 data = (utf8) ? utf8Encode(data) : data;
37508                 var hash, i = 0,
37509                   bkey = rstr2binb(key),
37510                   ipad = Array(16),
37511                   opad = Array(16);
37512
37513                 if (bkey.length > 16) {
37514                   bkey = binb(bkey, key.length * 8);
37515                 }
37516
37517                 for (; i < 16; i += 1) {
37518                   ipad[i] = bkey[i] ^ 0x36363636;
37519                   opad[i] = bkey[i] ^ 0x5C5C5C5C;
37520                 }
37521
37522                 hash = binb(ipad.concat(rstr2binb(data)), 512 + data.length * 8);
37523                 return binb2rstr(binb(opad.concat(hash), 512 + 256));
37524               }
37525
37526               /*
37527                * Main sha256 function, with its support functions
37528                */
37529
37530               function sha256_S(X, n) {
37531                 return (X >>> n) | (X << (32 - n));
37532               }
37533
37534               function sha256_R(X, n) {
37535                 return (X >>> n);
37536               }
37537
37538               function sha256_Ch(x, y, z) {
37539                 return ((x & y) ^ ((~x) & z));
37540               }
37541
37542               function sha256_Maj(x, y, z) {
37543                 return ((x & y) ^ (x & z) ^ (y & z));
37544               }
37545
37546               function sha256_Sigma0256(x) {
37547                 return (sha256_S(x, 2) ^ sha256_S(x, 13) ^ sha256_S(x, 22));
37548               }
37549
37550               function sha256_Sigma1256(x) {
37551                 return (sha256_S(x, 6) ^ sha256_S(x, 11) ^ sha256_S(x, 25));
37552               }
37553
37554               function sha256_Gamma0256(x) {
37555                 return (sha256_S(x, 7) ^ sha256_S(x, 18) ^ sha256_R(x, 3));
37556               }
37557
37558               function sha256_Gamma1256(x) {
37559                 return (sha256_S(x, 17) ^ sha256_S(x, 19) ^ sha256_R(x, 10));
37560               }
37561
37562               sha256_K = [
37563                 1116352408, 1899447441, -1245643825, -373957723, 961987163, 1508970993, -1841331548, -1424204075, -670586216, 310598401, 607225278, 1426881987,
37564                 1925078388, -2132889090, -1680079193, -1046744716, -459576895, -272742522,
37565                 264347078, 604807628, 770255983, 1249150122, 1555081692, 1996064986, -1740746414, -1473132947, -1341970488, -1084653625, -958395405, -710438585,
37566                 113926993, 338241895, 666307205, 773529912, 1294757372, 1396182291,
37567                 1695183700, 1986661051, -2117940946, -1838011259, -1564481375, -1474664885, -1035236496, -949202525, -778901479, -694614492, -200395387, 275423344,
37568                 430227734, 506948616, 659060556, 883997877, 958139571, 1322822218,
37569                 1537002063, 1747873779, 1955562222, 2024104815, -2067236844, -1933114872, -1866530822, -1538233109, -1090935817, -965641998
37570               ];
37571
37572               function binb(m, l) {
37573                 var HASH = [1779033703, -1150833019, 1013904242, -1521486534,
37574                   1359893119, -1694144372, 528734635, 1541459225
37575                 ];
37576                 var W = new Array(64);
37577                 var a, b, c, d, e, f, g, h;
37578                 var i, j, T1, T2;
37579
37580                 /* append padding */
37581                 m[l >> 5] |= 0x80 << (24 - l % 32);
37582                 m[((l + 64 >> 9) << 4) + 15] = l;
37583
37584                 for (i = 0; i < m.length; i += 16) {
37585                   a = HASH[0];
37586                   b = HASH[1];
37587                   c = HASH[2];
37588                   d = HASH[3];
37589                   e = HASH[4];
37590                   f = HASH[5];
37591                   g = HASH[6];
37592                   h = HASH[7];
37593
37594                   for (j = 0; j < 64; j += 1) {
37595                     if (j < 16) {
37596                       W[j] = m[j + i];
37597                     } else {
37598                       W[j] = safe_add(safe_add(safe_add(sha256_Gamma1256(W[j - 2]), W[j - 7]),
37599                         sha256_Gamma0256(W[j - 15])), W[j - 16]);
37600                     }
37601
37602                     T1 = safe_add(safe_add(safe_add(safe_add(h, sha256_Sigma1256(e)), sha256_Ch(e, f, g)),
37603                       sha256_K[j]), W[j]);
37604                     T2 = safe_add(sha256_Sigma0256(a), sha256_Maj(a, b, c));
37605                     h = g;
37606                     g = f;
37607                     f = e;
37608                     e = safe_add(d, T1);
37609                     d = c;
37610                     c = b;
37611                     b = a;
37612                     a = safe_add(T1, T2);
37613                   }
37614
37615                   HASH[0] = safe_add(a, HASH[0]);
37616                   HASH[1] = safe_add(b, HASH[1]);
37617                   HASH[2] = safe_add(c, HASH[2]);
37618                   HASH[3] = safe_add(d, HASH[3]);
37619                   HASH[4] = safe_add(e, HASH[4]);
37620                   HASH[5] = safe_add(f, HASH[5]);
37621                   HASH[6] = safe_add(g, HASH[6]);
37622                   HASH[7] = safe_add(h, HASH[7]);
37623                 }
37624                 return HASH;
37625               }
37626
37627             },
37628
37629             /**
37630              * @class Hashes.SHA512
37631              * @param {config}
37632              *
37633              * A JavaScript implementation of the Secure Hash Algorithm, SHA-512, as defined in FIPS 180-2
37634              * Version 2.2 Copyright Anonymous Contributor, Paul Johnston 2000 - 2009.
37635              * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
37636              * See http://pajhome.org.uk/crypt/md5 for details.
37637              */
37638             SHA512: function(options) {
37639               /**
37640                * Private properties configuration variables. You may need to tweak these to be compatible with
37641                * the server-side, but the defaults work in most cases.
37642                * @see this.setUpperCase() method
37643                * @see this.setPad() method
37644                */
37645               var hexcase = (options && typeof options.uppercase === 'boolean') ? options.uppercase : false,
37646                 /* hexadecimal output case format. false - lowercase; true - uppercase  */
37647                 b64pad = (options && typeof options.pad === 'string') ? options.pad : '=',
37648                 /* base-64 pad character. Default '=' for strict RFC compliance   */
37649                 utf8 = (options && typeof options.utf8 === 'boolean') ? options.utf8 : true,
37650                 /* enable/disable utf8 encoding */
37651                 sha512_k;
37652
37653               /* privileged (public) methods */
37654               this.hex = function(s) {
37655                 return rstr2hex(rstr(s));
37656               };
37657               this.b64 = function(s) {
37658                 return rstr2b64(rstr(s), b64pad);
37659               };
37660               this.any = function(s, e) {
37661                 return rstr2any(rstr(s), e);
37662               };
37663               this.raw = function(s) {
37664                 return rstr(s);
37665               };
37666               this.hex_hmac = function(k, d) {
37667                 return rstr2hex(rstr_hmac(k, d));
37668               };
37669               this.b64_hmac = function(k, d) {
37670                 return rstr2b64(rstr_hmac(k, d), b64pad);
37671               };
37672               this.any_hmac = function(k, d, e) {
37673                 return rstr2any(rstr_hmac(k, d), e);
37674               };
37675               /**
37676                * Perform a simple self-test to see if the VM is working
37677                * @return {String} Hexadecimal hash sample
37678                * @public
37679                */
37680               this.vm_test = function() {
37681                 return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72';
37682               };
37683               /**
37684                * @description Enable/disable uppercase hexadecimal returned string
37685                * @param {boolean}
37686                * @return {Object} this
37687                * @public
37688                */
37689               this.setUpperCase = function(a) {
37690                 if (typeof a === 'boolean') {
37691                   hexcase = a;
37692                 }
37693                 return this;
37694               };
37695               /**
37696                * @description Defines a base64 pad string
37697                * @param {string} Pad
37698                * @return {Object} this
37699                * @public
37700                */
37701               this.setPad = function(a) {
37702                 b64pad = a || b64pad;
37703                 return this;
37704               };
37705               /**
37706                * @description Defines a base64 pad string
37707                * @param {boolean}
37708                * @return {Object} this
37709                * @public
37710                */
37711               this.setUTF8 = function(a) {
37712                 if (typeof a === 'boolean') {
37713                   utf8 = a;
37714                 }
37715                 return this;
37716               };
37717
37718               /* private methods */
37719
37720               /**
37721                * Calculate the SHA-512 of a raw string
37722                */
37723
37724               function rstr(s) {
37725                 s = (utf8) ? utf8Encode(s) : s;
37726                 return binb2rstr(binb(rstr2binb(s), s.length * 8));
37727               }
37728               /*
37729                * Calculate the HMAC-SHA-512 of a key and some data (raw strings)
37730                */
37731
37732               function rstr_hmac(key, data) {
37733                 key = (utf8) ? utf8Encode(key) : key;
37734                 data = (utf8) ? utf8Encode(data) : data;
37735
37736                 var hash, i = 0,
37737                   bkey = rstr2binb(key),
37738                   ipad = Array(32),
37739                   opad = Array(32);
37740
37741                 if (bkey.length > 32) {
37742                   bkey = binb(bkey, key.length * 8);
37743                 }
37744
37745                 for (; i < 32; i += 1) {
37746                   ipad[i] = bkey[i] ^ 0x36363636;
37747                   opad[i] = bkey[i] ^ 0x5C5C5C5C;
37748                 }
37749
37750                 hash = binb(ipad.concat(rstr2binb(data)), 1024 + data.length * 8);
37751                 return binb2rstr(binb(opad.concat(hash), 1024 + 512));
37752               }
37753
37754               /**
37755                * Calculate the SHA-512 of an array of big-endian dwords, and a bit length
37756                */
37757
37758               function binb(x, len) {
37759                 var j, i, l,
37760                   W = new Array(80),
37761                   hash = new Array(16),
37762                   //Initial hash values
37763                   H = [
37764                     new int64(0x6a09e667, -205731576),
37765                     new int64(-1150833019, -2067093701),
37766                     new int64(0x3c6ef372, -23791573),
37767                     new int64(-1521486534, 0x5f1d36f1),
37768                     new int64(0x510e527f, -1377402159),
37769                     new int64(-1694144372, 0x2b3e6c1f),
37770                     new int64(0x1f83d9ab, -79577749),
37771                     new int64(0x5be0cd19, 0x137e2179)
37772                   ],
37773                   T1 = new int64(0, 0),
37774                   T2 = new int64(0, 0),
37775                   a = new int64(0, 0),
37776                   b = new int64(0, 0),
37777                   c = new int64(0, 0),
37778                   d = new int64(0, 0),
37779                   e = new int64(0, 0),
37780                   f = new int64(0, 0),
37781                   g = new int64(0, 0),
37782                   h = new int64(0, 0),
37783                   //Temporary variables not specified by the document
37784                   s0 = new int64(0, 0),
37785                   s1 = new int64(0, 0),
37786                   Ch = new int64(0, 0),
37787                   Maj = new int64(0, 0),
37788                   r1 = new int64(0, 0),
37789                   r2 = new int64(0, 0),
37790                   r3 = new int64(0, 0);
37791
37792                 if (sha512_k === undefined) {
37793                   //SHA512 constants
37794                   sha512_k = [
37795                     new int64(0x428a2f98, -685199838), new int64(0x71374491, 0x23ef65cd),
37796                     new int64(-1245643825, -330482897), new int64(-373957723, -2121671748),
37797                     new int64(0x3956c25b, -213338824), new int64(0x59f111f1, -1241133031),
37798                     new int64(-1841331548, -1357295717), new int64(-1424204075, -630357736),
37799                     new int64(-670586216, -1560083902), new int64(0x12835b01, 0x45706fbe),
37800                     new int64(0x243185be, 0x4ee4b28c), new int64(0x550c7dc3, -704662302),
37801                     new int64(0x72be5d74, -226784913), new int64(-2132889090, 0x3b1696b1),
37802                     new int64(-1680079193, 0x25c71235), new int64(-1046744716, -815192428),
37803                     new int64(-459576895, -1628353838), new int64(-272742522, 0x384f25e3),
37804                     new int64(0xfc19dc6, -1953704523), new int64(0x240ca1cc, 0x77ac9c65),
37805                     new int64(0x2de92c6f, 0x592b0275), new int64(0x4a7484aa, 0x6ea6e483),
37806                     new int64(0x5cb0a9dc, -1119749164), new int64(0x76f988da, -2096016459),
37807                     new int64(-1740746414, -295247957), new int64(-1473132947, 0x2db43210),
37808                     new int64(-1341970488, -1728372417), new int64(-1084653625, -1091629340),
37809                     new int64(-958395405, 0x3da88fc2), new int64(-710438585, -1828018395),
37810                     new int64(0x6ca6351, -536640913), new int64(0x14292967, 0xa0e6e70),
37811                     new int64(0x27b70a85, 0x46d22ffc), new int64(0x2e1b2138, 0x5c26c926),
37812                     new int64(0x4d2c6dfc, 0x5ac42aed), new int64(0x53380d13, -1651133473),
37813                     new int64(0x650a7354, -1951439906), new int64(0x766a0abb, 0x3c77b2a8),
37814                     new int64(-2117940946, 0x47edaee6), new int64(-1838011259, 0x1482353b),
37815                     new int64(-1564481375, 0x4cf10364), new int64(-1474664885, -1136513023),
37816                     new int64(-1035236496, -789014639), new int64(-949202525, 0x654be30),
37817                     new int64(-778901479, -688958952), new int64(-694614492, 0x5565a910),
37818                     new int64(-200395387, 0x5771202a), new int64(0x106aa070, 0x32bbd1b8),
37819                     new int64(0x19a4c116, -1194143544), new int64(0x1e376c08, 0x5141ab53),
37820                     new int64(0x2748774c, -544281703), new int64(0x34b0bcb5, -509917016),
37821                     new int64(0x391c0cb3, -976659869), new int64(0x4ed8aa4a, -482243893),
37822                     new int64(0x5b9cca4f, 0x7763e373), new int64(0x682e6ff3, -692930397),
37823                     new int64(0x748f82ee, 0x5defb2fc), new int64(0x78a5636f, 0x43172f60),
37824                     new int64(-2067236844, -1578062990), new int64(-1933114872, 0x1a6439ec),
37825                     new int64(-1866530822, 0x23631e28), new int64(-1538233109, -561857047),
37826                     new int64(-1090935817, -1295615723), new int64(-965641998, -479046869),
37827                     new int64(-903397682, -366583396), new int64(-779700025, 0x21c0c207),
37828                     new int64(-354779690, -840897762), new int64(-176337025, -294727304),
37829                     new int64(0x6f067aa, 0x72176fba), new int64(0xa637dc5, -1563912026),
37830                     new int64(0x113f9804, -1090974290), new int64(0x1b710b35, 0x131c471b),
37831                     new int64(0x28db77f5, 0x23047d84), new int64(0x32caab7b, 0x40c72493),
37832                     new int64(0x3c9ebe0a, 0x15c9bebc), new int64(0x431d67c4, -1676669620),
37833                     new int64(0x4cc5d4be, -885112138), new int64(0x597f299c, -60457430),
37834                     new int64(0x5fcb6fab, 0x3ad6faec), new int64(0x6c44198c, 0x4a475817)
37835                   ];
37836                 }
37837
37838                 for (i = 0; i < 80; i += 1) {
37839                   W[i] = new int64(0, 0);
37840                 }
37841
37842                 // append padding to the source string. The format is described in the FIPS.
37843                 x[len >> 5] |= 0x80 << (24 - (len & 0x1f));
37844                 x[((len + 128 >> 10) << 5) + 31] = len;
37845                 l = x.length;
37846                 for (i = 0; i < l; i += 32) { //32 dwords is the block size
37847                   int64copy(a, H[0]);
37848                   int64copy(b, H[1]);
37849                   int64copy(c, H[2]);
37850                   int64copy(d, H[3]);
37851                   int64copy(e, H[4]);
37852                   int64copy(f, H[5]);
37853                   int64copy(g, H[6]);
37854                   int64copy(h, H[7]);
37855
37856                   for (j = 0; j < 16; j += 1) {
37857                     W[j].h = x[i + 2 * j];
37858                     W[j].l = x[i + 2 * j + 1];
37859                   }
37860
37861                   for (j = 16; j < 80; j += 1) {
37862                     //sigma1
37863                     int64rrot(r1, W[j - 2], 19);
37864                     int64revrrot(r2, W[j - 2], 29);
37865                     int64shr(r3, W[j - 2], 6);
37866                     s1.l = r1.l ^ r2.l ^ r3.l;
37867                     s1.h = r1.h ^ r2.h ^ r3.h;
37868                     //sigma0
37869                     int64rrot(r1, W[j - 15], 1);
37870                     int64rrot(r2, W[j - 15], 8);
37871                     int64shr(r3, W[j - 15], 7);
37872                     s0.l = r1.l ^ r2.l ^ r3.l;
37873                     s0.h = r1.h ^ r2.h ^ r3.h;
37874
37875                     int64add4(W[j], s1, W[j - 7], s0, W[j - 16]);
37876                   }
37877
37878                   for (j = 0; j < 80; j += 1) {
37879                     //Ch
37880                     Ch.l = (e.l & f.l) ^ (~e.l & g.l);
37881                     Ch.h = (e.h & f.h) ^ (~e.h & g.h);
37882
37883                     //Sigma1
37884                     int64rrot(r1, e, 14);
37885                     int64rrot(r2, e, 18);
37886                     int64revrrot(r3, e, 9);
37887                     s1.l = r1.l ^ r2.l ^ r3.l;
37888                     s1.h = r1.h ^ r2.h ^ r3.h;
37889
37890                     //Sigma0
37891                     int64rrot(r1, a, 28);
37892                     int64revrrot(r2, a, 2);
37893                     int64revrrot(r3, a, 7);
37894                     s0.l = r1.l ^ r2.l ^ r3.l;
37895                     s0.h = r1.h ^ r2.h ^ r3.h;
37896
37897                     //Maj
37898                     Maj.l = (a.l & b.l) ^ (a.l & c.l) ^ (b.l & c.l);
37899                     Maj.h = (a.h & b.h) ^ (a.h & c.h) ^ (b.h & c.h);
37900
37901                     int64add5(T1, h, s1, Ch, sha512_k[j], W[j]);
37902                     int64add(T2, s0, Maj);
37903
37904                     int64copy(h, g);
37905                     int64copy(g, f);
37906                     int64copy(f, e);
37907                     int64add(e, d, T1);
37908                     int64copy(d, c);
37909                     int64copy(c, b);
37910                     int64copy(b, a);
37911                     int64add(a, T1, T2);
37912                   }
37913                   int64add(H[0], H[0], a);
37914                   int64add(H[1], H[1], b);
37915                   int64add(H[2], H[2], c);
37916                   int64add(H[3], H[3], d);
37917                   int64add(H[4], H[4], e);
37918                   int64add(H[5], H[5], f);
37919                   int64add(H[6], H[6], g);
37920                   int64add(H[7], H[7], h);
37921                 }
37922
37923                 //represent the hash as an array of 32-bit dwords
37924                 for (i = 0; i < 8; i += 1) {
37925                   hash[2 * i] = H[i].h;
37926                   hash[2 * i + 1] = H[i].l;
37927                 }
37928                 return hash;
37929               }
37930
37931               //A constructor for 64-bit numbers
37932
37933               function int64(h, l) {
37934                 this.h = h;
37935                 this.l = l;
37936                 //this.toString = int64toString;
37937               }
37938
37939               //Copies src into dst, assuming both are 64-bit numbers
37940
37941               function int64copy(dst, src) {
37942                 dst.h = src.h;
37943                 dst.l = src.l;
37944               }
37945
37946               //Right-rotates a 64-bit number by shift
37947               //Won't handle cases of shift>=32
37948               //The function revrrot() is for that
37949
37950               function int64rrot(dst, x, shift) {
37951                 dst.l = (x.l >>> shift) | (x.h << (32 - shift));
37952                 dst.h = (x.h >>> shift) | (x.l << (32 - shift));
37953               }
37954
37955               //Reverses the dwords of the source and then rotates right by shift.
37956               //This is equivalent to rotation by 32+shift
37957
37958               function int64revrrot(dst, x, shift) {
37959                 dst.l = (x.h >>> shift) | (x.l << (32 - shift));
37960                 dst.h = (x.l >>> shift) | (x.h << (32 - shift));
37961               }
37962
37963               //Bitwise-shifts right a 64-bit number by shift
37964               //Won't handle shift>=32, but it's never needed in SHA512
37965
37966               function int64shr(dst, x, shift) {
37967                 dst.l = (x.l >>> shift) | (x.h << (32 - shift));
37968                 dst.h = (x.h >>> shift);
37969               }
37970
37971               //Adds two 64-bit numbers
37972               //Like the original implementation, does not rely on 32-bit operations
37973
37974               function int64add(dst, x, y) {
37975                 var w0 = (x.l & 0xffff) + (y.l & 0xffff);
37976                 var w1 = (x.l >>> 16) + (y.l >>> 16) + (w0 >>> 16);
37977                 var w2 = (x.h & 0xffff) + (y.h & 0xffff) + (w1 >>> 16);
37978                 var w3 = (x.h >>> 16) + (y.h >>> 16) + (w2 >>> 16);
37979                 dst.l = (w0 & 0xffff) | (w1 << 16);
37980                 dst.h = (w2 & 0xffff) | (w3 << 16);
37981               }
37982
37983               //Same, except with 4 addends. Works faster than adding them one by one.
37984
37985               function int64add4(dst, a, b, c, d) {
37986                 var w0 = (a.l & 0xffff) + (b.l & 0xffff) + (c.l & 0xffff) + (d.l & 0xffff);
37987                 var w1 = (a.l >>> 16) + (b.l >>> 16) + (c.l >>> 16) + (d.l >>> 16) + (w0 >>> 16);
37988                 var w2 = (a.h & 0xffff) + (b.h & 0xffff) + (c.h & 0xffff) + (d.h & 0xffff) + (w1 >>> 16);
37989                 var w3 = (a.h >>> 16) + (b.h >>> 16) + (c.h >>> 16) + (d.h >>> 16) + (w2 >>> 16);
37990                 dst.l = (w0 & 0xffff) | (w1 << 16);
37991                 dst.h = (w2 & 0xffff) | (w3 << 16);
37992               }
37993
37994               //Same, except with 5 addends
37995
37996               function int64add5(dst, a, b, c, d, e) {
37997                 var w0 = (a.l & 0xffff) + (b.l & 0xffff) + (c.l & 0xffff) + (d.l & 0xffff) + (e.l & 0xffff),
37998                   w1 = (a.l >>> 16) + (b.l >>> 16) + (c.l >>> 16) + (d.l >>> 16) + (e.l >>> 16) + (w0 >>> 16),
37999                   w2 = (a.h & 0xffff) + (b.h & 0xffff) + (c.h & 0xffff) + (d.h & 0xffff) + (e.h & 0xffff) + (w1 >>> 16),
38000                   w3 = (a.h >>> 16) + (b.h >>> 16) + (c.h >>> 16) + (d.h >>> 16) + (e.h >>> 16) + (w2 >>> 16);
38001                 dst.l = (w0 & 0xffff) | (w1 << 16);
38002                 dst.h = (w2 & 0xffff) | (w3 << 16);
38003               }
38004             },
38005             /**
38006              * @class Hashes.RMD160
38007              * @constructor
38008              * @param {Object} [config]
38009              *
38010              * A JavaScript implementation of the RIPEMD-160 Algorithm
38011              * Version 2.2 Copyright Jeremy Lin, Paul Johnston 2000 - 2009.
38012              * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
38013              * See http://pajhome.org.uk/crypt/md5 for details.
38014              * Also http://www.ocf.berkeley.edu/~jjlin/jsotp/
38015              */
38016             RMD160: function(options) {
38017               /**
38018                * Private properties configuration variables. You may need to tweak these to be compatible with
38019                * the server-side, but the defaults work in most cases.
38020                * @see this.setUpperCase() method
38021                * @see this.setPad() method
38022                */
38023               var hexcase = (options && typeof options.uppercase === 'boolean') ? options.uppercase : false,
38024                 /* hexadecimal output case format. false - lowercase; true - uppercase  */
38025                 b64pad = (options && typeof options.pad === 'string') ? options.pa : '=',
38026                 /* base-64 pad character. Default '=' for strict RFC compliance   */
38027                 utf8 = (options && typeof options.utf8 === 'boolean') ? options.utf8 : true,
38028                 /* enable/disable utf8 encoding */
38029                 rmd160_r1 = [
38030                   0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
38031                   7, 4, 13, 1, 10, 6, 15, 3, 12, 0, 9, 5, 2, 14, 11, 8,
38032                   3, 10, 14, 4, 9, 15, 8, 1, 2, 7, 0, 6, 13, 11, 5, 12,
38033                   1, 9, 11, 10, 0, 8, 12, 4, 13, 3, 7, 15, 14, 5, 6, 2,
38034                   4, 0, 5, 9, 7, 12, 2, 10, 14, 1, 3, 8, 11, 6, 15, 13
38035                 ],
38036                 rmd160_r2 = [
38037                   5, 14, 7, 0, 9, 2, 11, 4, 13, 6, 15, 8, 1, 10, 3, 12,
38038                   6, 11, 3, 7, 0, 13, 5, 10, 14, 15, 8, 12, 4, 9, 1, 2,
38039                   15, 5, 1, 3, 7, 14, 6, 9, 11, 8, 12, 2, 10, 0, 4, 13,
38040                   8, 6, 4, 1, 3, 11, 15, 0, 5, 12, 2, 13, 9, 7, 10, 14,
38041                   12, 15, 10, 4, 1, 5, 8, 7, 6, 2, 13, 14, 0, 3, 9, 11
38042                 ],
38043                 rmd160_s1 = [
38044                   11, 14, 15, 12, 5, 8, 7, 9, 11, 13, 14, 15, 6, 7, 9, 8,
38045                   7, 6, 8, 13, 11, 9, 7, 15, 7, 12, 15, 9, 11, 7, 13, 12,
38046                   11, 13, 6, 7, 14, 9, 13, 15, 14, 8, 13, 6, 5, 12, 7, 5,
38047                   11, 12, 14, 15, 14, 15, 9, 8, 9, 14, 5, 6, 8, 6, 5, 12,
38048                   9, 15, 5, 11, 6, 8, 13, 12, 5, 12, 13, 14, 11, 8, 5, 6
38049                 ],
38050                 rmd160_s2 = [
38051                   8, 9, 9, 11, 13, 15, 15, 5, 7, 7, 8, 11, 14, 14, 12, 6,
38052                   9, 13, 15, 7, 12, 8, 9, 11, 7, 7, 12, 7, 6, 15, 13, 11,
38053                   9, 7, 15, 11, 8, 6, 6, 14, 12, 13, 5, 14, 13, 13, 7, 5,
38054                   15, 5, 8, 11, 14, 14, 6, 14, 6, 9, 12, 9, 12, 5, 15, 8,
38055                   8, 5, 12, 9, 12, 5, 14, 6, 8, 13, 6, 5, 15, 13, 11, 11
38056                 ];
38057
38058               /* privileged (public) methods */
38059               this.hex = function(s) {
38060                 return rstr2hex(rstr(s));
38061               };
38062               this.b64 = function(s) {
38063                 return rstr2b64(rstr(s), b64pad);
38064               };
38065               this.any = function(s, e) {
38066                 return rstr2any(rstr(s), e);
38067               };
38068               this.raw = function(s) {
38069                 return rstr(s);
38070               };
38071               this.hex_hmac = function(k, d) {
38072                 return rstr2hex(rstr_hmac(k, d));
38073               };
38074               this.b64_hmac = function(k, d) {
38075                 return rstr2b64(rstr_hmac(k, d), b64pad);
38076               };
38077               this.any_hmac = function(k, d, e) {
38078                 return rstr2any(rstr_hmac(k, d), e);
38079               };
38080               /**
38081                * Perform a simple self-test to see if the VM is working
38082                * @return {String} Hexadecimal hash sample
38083                * @public
38084                */
38085               this.vm_test = function() {
38086                 return hex('abc').toLowerCase() === '900150983cd24fb0d6963f7d28e17f72';
38087               };
38088               /**
38089                * @description Enable/disable uppercase hexadecimal returned string
38090                * @param {boolean}
38091                * @return {Object} this
38092                * @public
38093                */
38094               this.setUpperCase = function(a) {
38095                 if (typeof a === 'boolean') {
38096                   hexcase = a;
38097                 }
38098                 return this;
38099               };
38100               /**
38101                * @description Defines a base64 pad string
38102                * @param {string} Pad
38103                * @return {Object} this
38104                * @public
38105                */
38106               this.setPad = function(a) {
38107                 if (typeof a !== 'undefined') {
38108                   b64pad = a;
38109                 }
38110                 return this;
38111               };
38112               /**
38113                * @description Defines a base64 pad string
38114                * @param {boolean}
38115                * @return {Object} this
38116                * @public
38117                */
38118               this.setUTF8 = function(a) {
38119                 if (typeof a === 'boolean') {
38120                   utf8 = a;
38121                 }
38122                 return this;
38123               };
38124
38125               /* private methods */
38126
38127               /**
38128                * Calculate the rmd160 of a raw string
38129                */
38130
38131               function rstr(s) {
38132                 s = (utf8) ? utf8Encode(s) : s;
38133                 return binl2rstr(binl(rstr2binl(s), s.length * 8));
38134               }
38135
38136               /**
38137                * Calculate the HMAC-rmd160 of a key and some data (raw strings)
38138                */
38139
38140               function rstr_hmac(key, data) {
38141                 key = (utf8) ? utf8Encode(key) : key;
38142                 data = (utf8) ? utf8Encode(data) : data;
38143                 var i, hash,
38144                   bkey = rstr2binl(key),
38145                   ipad = Array(16),
38146                   opad = Array(16);
38147
38148                 if (bkey.length > 16) {
38149                   bkey = binl(bkey, key.length * 8);
38150                 }
38151
38152                 for (i = 0; i < 16; i += 1) {
38153                   ipad[i] = bkey[i] ^ 0x36363636;
38154                   opad[i] = bkey[i] ^ 0x5C5C5C5C;
38155                 }
38156                 hash = binl(ipad.concat(rstr2binl(data)), 512 + data.length * 8);
38157                 return binl2rstr(binl(opad.concat(hash), 512 + 160));
38158               }
38159
38160               /**
38161                * Convert an array of little-endian words to a string
38162                */
38163
38164               function binl2rstr(input) {
38165                 var i, output = '',
38166                   l = input.length * 32;
38167                 for (i = 0; i < l; i += 8) {
38168                   output += String.fromCharCode((input[i >> 5] >>> (i % 32)) & 0xFF);
38169                 }
38170                 return output;
38171               }
38172
38173               /**
38174                * Calculate the RIPE-MD160 of an array of little-endian words, and a bit length.
38175                */
38176
38177               function binl(x, len) {
38178                 var T, j, i, l,
38179                   h0 = 0x67452301,
38180                   h1 = 0xefcdab89,
38181                   h2 = 0x98badcfe,
38182                   h3 = 0x10325476,
38183                   h4 = 0xc3d2e1f0,
38184                   A1, B1, C1, D1, E1,
38185                   A2, B2, C2, D2, E2;
38186
38187                 /* append padding */
38188                 x[len >> 5] |= 0x80 << (len % 32);
38189                 x[(((len + 64) >>> 9) << 4) + 14] = len;
38190                 l = x.length;
38191
38192                 for (i = 0; i < l; i += 16) {
38193                   A1 = A2 = h0;
38194                   B1 = B2 = h1;
38195                   C1 = C2 = h2;
38196                   D1 = D2 = h3;
38197                   E1 = E2 = h4;
38198                   for (j = 0; j <= 79; j += 1) {
38199                     T = safe_add(A1, rmd160_f(j, B1, C1, D1));
38200                     T = safe_add(T, x[i + rmd160_r1[j]]);
38201                     T = safe_add(T, rmd160_K1(j));
38202                     T = safe_add(bit_rol(T, rmd160_s1[j]), E1);
38203                     A1 = E1;
38204                     E1 = D1;
38205                     D1 = bit_rol(C1, 10);
38206                     C1 = B1;
38207                     B1 = T;
38208                     T = safe_add(A2, rmd160_f(79 - j, B2, C2, D2));
38209                     T = safe_add(T, x[i + rmd160_r2[j]]);
38210                     T = safe_add(T, rmd160_K2(j));
38211                     T = safe_add(bit_rol(T, rmd160_s2[j]), E2);
38212                     A2 = E2;
38213                     E2 = D2;
38214                     D2 = bit_rol(C2, 10);
38215                     C2 = B2;
38216                     B2 = T;
38217                   }
38218
38219                   T = safe_add(h1, safe_add(C1, D2));
38220                   h1 = safe_add(h2, safe_add(D1, E2));
38221                   h2 = safe_add(h3, safe_add(E1, A2));
38222                   h3 = safe_add(h4, safe_add(A1, B2));
38223                   h4 = safe_add(h0, safe_add(B1, C2));
38224                   h0 = T;
38225                 }
38226                 return [h0, h1, h2, h3, h4];
38227               }
38228
38229               // specific algorithm methods
38230
38231               function rmd160_f(j, x, y, z) {
38232                 return (0 <= j && j <= 15) ? (x ^ y ^ z) :
38233                   (16 <= j && j <= 31) ? (x & y) | (~x & z) :
38234                   (32 <= j && j <= 47) ? (x | ~y) ^ z :
38235                   (48 <= j && j <= 63) ? (x & z) | (y & ~z) :
38236                   (64 <= j && j <= 79) ? x ^ (y | ~z) :
38237                   'rmd160_f: j out of range';
38238               }
38239
38240               function rmd160_K1(j) {
38241                 return (0 <= j && j <= 15) ? 0x00000000 :
38242                   (16 <= j && j <= 31) ? 0x5a827999 :
38243                   (32 <= j && j <= 47) ? 0x6ed9eba1 :
38244                   (48 <= j && j <= 63) ? 0x8f1bbcdc :
38245                   (64 <= j && j <= 79) ? 0xa953fd4e :
38246                   'rmd160_K1: j out of range';
38247               }
38248
38249               function rmd160_K2(j) {
38250                 return (0 <= j && j <= 15) ? 0x50a28be6 :
38251                   (16 <= j && j <= 31) ? 0x5c4dd124 :
38252                   (32 <= j && j <= 47) ? 0x6d703ef3 :
38253                   (48 <= j && j <= 63) ? 0x7a6d76e9 :
38254                   (64 <= j && j <= 79) ? 0x00000000 :
38255                   'rmd160_K2: j out of range';
38256               }
38257             }
38258           };
38259
38260           // exposes Hashes
38261           (function(window, undefined$1) {
38262             var freeExports = false;
38263             {
38264               freeExports = exports;
38265               if (exports && typeof commonjsGlobal === 'object' && commonjsGlobal && commonjsGlobal === commonjsGlobal.global) {
38266                 window = commonjsGlobal;
38267               }
38268             }
38269
38270             if (typeof undefined$1 === 'function' && typeof undefined$1.amd === 'object' && undefined$1.amd) {
38271               // define as an anonymous module, so, through path mapping, it can be aliased
38272               undefined$1(function() {
38273                 return Hashes;
38274               });
38275             } else if (freeExports) {
38276               // in Node.js or RingoJS v0.8.0+
38277               if ( module && module.exports === freeExports) {
38278                 module.exports = Hashes;
38279               }
38280               // in Narwhal or RingoJS v0.7.0-
38281               else {
38282                 freeExports.Hashes = Hashes;
38283               }
38284             } else {
38285               // in a browser or Rhino
38286               window.Hashes = Hashes;
38287             }
38288           }(this));
38289         }()); // IIFE
38290         });
38291
38292         var immutable = extend$2;
38293
38294         var hasOwnProperty$3 = Object.prototype.hasOwnProperty;
38295
38296         function extend$2() {
38297             var arguments$1 = arguments;
38298
38299             var target = {};
38300
38301             for (var i = 0; i < arguments.length; i++) {
38302                 var source = arguments$1[i];
38303
38304                 for (var key in source) {
38305                     if (hasOwnProperty$3.call(source, key)) {
38306                         target[key] = source[key];
38307                     }
38308                 }
38309             }
38310
38311             return target
38312         }
38313
38314         var sha1 = new hashes.SHA1();
38315
38316         var ohauth = {};
38317
38318         ohauth.qsString = function(obj) {
38319             return Object.keys(obj).sort().map(function(key) {
38320                 return ohauth.percentEncode(key) + '=' +
38321                     ohauth.percentEncode(obj[key]);
38322             }).join('&');
38323         };
38324
38325         ohauth.stringQs = function(str) {
38326             return str.split('&').filter(function (pair) {
38327                 return pair !== '';
38328             }).reduce(function(obj, pair){
38329                 var parts = pair.split('=');
38330                 obj[decodeURIComponent(parts[0])] = (null === parts[1]) ?
38331                     '' : decodeURIComponent(parts[1]);
38332                 return obj;
38333             }, {});
38334         };
38335
38336         ohauth.rawxhr = function(method, url, data, headers, callback) {
38337             var xhr = new XMLHttpRequest(),
38338                 twoHundred = /^20\d$/;
38339             xhr.onreadystatechange = function() {
38340                 if (4 === xhr.readyState && 0 !== xhr.status) {
38341                     if (twoHundred.test(xhr.status)) { callback(null, xhr); }
38342                     else { return callback(xhr, null); }
38343                 }
38344             };
38345             xhr.onerror = function(e) { return callback(e, null); };
38346             xhr.open(method, url, true);
38347             for (var h in headers) { xhr.setRequestHeader(h, headers[h]); }
38348             xhr.send(data);
38349             return xhr;
38350         };
38351
38352         ohauth.xhr = function(method, url, auth, data, options, callback) {
38353             var headers = (options && options.header) || {
38354                 'Content-Type': 'application/x-www-form-urlencoded'
38355             };
38356             headers.Authorization = 'OAuth ' + ohauth.authHeader(auth);
38357             return ohauth.rawxhr(method, url, data, headers, callback);
38358         };
38359
38360         ohauth.nonce = function() {
38361             for (var o = ''; o.length < 6;) {
38362                 o += '0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz'[Math.floor(Math.random() * 61)];
38363             }
38364             return o;
38365         };
38366
38367         ohauth.authHeader = function(obj) {
38368             return Object.keys(obj).sort().map(function(key) {
38369                 return encodeURIComponent(key) + '="' + encodeURIComponent(obj[key]) + '"';
38370             }).join(', ');
38371         };
38372
38373         ohauth.timestamp = function() { return ~~((+new Date()) / 1000); };
38374
38375         ohauth.percentEncode = function(s) {
38376             return encodeURIComponent(s)
38377                 .replace(/\!/g, '%21').replace(/\'/g, '%27')
38378                 .replace(/\*/g, '%2A').replace(/\(/g, '%28').replace(/\)/g, '%29');
38379         };
38380
38381         ohauth.baseString = function(method, url, params) {
38382             if (params.oauth_signature) { delete params.oauth_signature; }
38383             return [
38384                 method,
38385                 ohauth.percentEncode(url),
38386                 ohauth.percentEncode(ohauth.qsString(params))].join('&');
38387         };
38388
38389         ohauth.signature = function(oauth_secret, token_secret, baseString) {
38390             return sha1.b64_hmac(
38391                 ohauth.percentEncode(oauth_secret) + '&' +
38392                 ohauth.percentEncode(token_secret),
38393                 baseString);
38394         };
38395
38396         /**
38397          * Takes an options object for configuration (consumer_key,
38398          * consumer_secret, version, signature_method, token, token_secret)
38399          * and returns a function that generates the Authorization header
38400          * for given data.
38401          *
38402          * The returned function takes these parameters:
38403          * - method: GET/POST/...
38404          * - uri: full URI with protocol, port, path and query string
38405          * - extra_params: any extra parameters (that are passed in the POST data),
38406          *   can be an object or a from-urlencoded string.
38407          *
38408          * Returned function returns full OAuth header with "OAuth" string in it.
38409          */
38410
38411         ohauth.headerGenerator = function(options) {
38412             options = options || {};
38413             var consumer_key = options.consumer_key || '',
38414                 consumer_secret = options.consumer_secret || '',
38415                 signature_method = options.signature_method || 'HMAC-SHA1',
38416                 version = options.version || '1.0',
38417                 token = options.token || '',
38418                 token_secret = options.token_secret || '';
38419
38420             return function(method, uri, extra_params) {
38421                 method = method.toUpperCase();
38422                 if (typeof extra_params === 'string' && extra_params.length > 0) {
38423                     extra_params = ohauth.stringQs(extra_params);
38424                 }
38425
38426                 var uri_parts = uri.split('?', 2),
38427                 base_uri = uri_parts[0];
38428
38429                 var query_params = uri_parts.length === 2 ?
38430                     ohauth.stringQs(uri_parts[1]) : {};
38431
38432                 var oauth_params = {
38433                     oauth_consumer_key: consumer_key,
38434                     oauth_signature_method: signature_method,
38435                     oauth_version: version,
38436                     oauth_timestamp: ohauth.timestamp(),
38437                     oauth_nonce: ohauth.nonce()
38438                 };
38439
38440                 if (token) { oauth_params.oauth_token = token; }
38441
38442                 var all_params = immutable({}, oauth_params, query_params, extra_params),
38443                     base_str = ohauth.baseString(method, base_uri, all_params);
38444
38445                 oauth_params.oauth_signature = ohauth.signature(consumer_secret, token_secret, base_str);
38446
38447                 return 'OAuth ' + ohauth.authHeader(oauth_params);
38448             };
38449         };
38450
38451         var ohauth_1 = ohauth;
38452
38453         var resolveUrl$1 = createCommonjsModule(function (module, exports) {
38454         // Copyright 2014 Simon Lydell
38455         // X11 (“MIT”) Licensed. (See LICENSE.)
38456
38457         void (function(root, factory) {
38458           {
38459             module.exports = factory();
38460           }
38461         }(commonjsGlobal, function() {
38462
38463           function resolveUrl(/* ...urls */) {
38464             var arguments$1 = arguments;
38465
38466             var numUrls = arguments.length;
38467
38468             if (numUrls === 0) {
38469               throw new Error("resolveUrl requires at least one argument; got none.")
38470             }
38471
38472             var base = document.createElement("base");
38473             base.href = arguments[0];
38474
38475             if (numUrls === 1) {
38476               return base.href
38477             }
38478
38479             var head = document.getElementsByTagName("head")[0];
38480             head.insertBefore(base, head.firstChild);
38481
38482             var a = document.createElement("a");
38483             var resolved;
38484
38485             for (var index = 1; index < numUrls; index++) {
38486               a.href = arguments$1[index];
38487               resolved = a.href;
38488               base.href = resolved;
38489             }
38490
38491             head.removeChild(base);
38492
38493             return resolved
38494           }
38495
38496           return resolveUrl
38497
38498         }));
38499         });
38500
38501         var assign$1 = make_assign();
38502         var create$8 = make_create();
38503         var trim = make_trim();
38504         var Global = (typeof window !== 'undefined' ? window : commonjsGlobal);
38505
38506         var util = {
38507                 assign: assign$1,
38508                 create: create$8,
38509                 trim: trim,
38510                 bind: bind$3,
38511                 slice: slice$5,
38512                 each: each,
38513                 map: map$5,
38514                 pluck: pluck,
38515                 isList: isList,
38516                 isFunction: isFunction$2,
38517                 isObject: isObject$2,
38518                 Global: Global
38519         };
38520
38521         function make_assign() {
38522                 if (Object.assign) {
38523                         return Object.assign
38524                 } else {
38525                         return function shimAssign(obj, props1, props2, etc) {
38526                                 var arguments$1 = arguments;
38527
38528                                 for (var i = 1; i < arguments.length; i++) {
38529                                         each(Object(arguments$1[i]), function(val, key) {
38530                                                 obj[key] = val;
38531                                         });
38532                                 }                       
38533                                 return obj
38534                         }
38535                 }
38536         }
38537
38538         function make_create() {
38539                 if (Object.create) {
38540                         return function create(obj, assignProps1, assignProps2, etc) {
38541                                 var assignArgsList = slice$5(arguments, 1);
38542                                 return assign$1.apply(this, [Object.create(obj)].concat(assignArgsList))
38543                         }
38544                 } else {
38545                         function F() {} // eslint-disable-line no-inner-declarations
38546                         return function create(obj, assignProps1, assignProps2, etc) {
38547                                 var assignArgsList = slice$5(arguments, 1);
38548                                 F.prototype = obj;
38549                                 return assign$1.apply(this, [new F()].concat(assignArgsList))
38550                         }
38551                 }
38552         }
38553
38554         function make_trim() {
38555                 if (String.prototype.trim) {
38556                         return function trim(str) {
38557                                 return String.prototype.trim.call(str)
38558                         }
38559                 } else {
38560                         return function trim(str) {
38561                                 return str.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '')
38562                         }
38563                 }
38564         }
38565
38566         function bind$3(obj, fn) {
38567                 return function() {
38568                         return fn.apply(obj, Array.prototype.slice.call(arguments, 0))
38569                 }
38570         }
38571
38572         function slice$5(arr, index) {
38573                 return Array.prototype.slice.call(arr, index || 0)
38574         }
38575
38576         function each(obj, fn) {
38577                 pluck(obj, function(val, key) {
38578                         fn(val, key);
38579                         return false
38580                 });
38581         }
38582
38583         function map$5(obj, fn) {
38584                 var res = (isList(obj) ? [] : {});
38585                 pluck(obj, function(v, k) {
38586                         res[k] = fn(v, k);
38587                         return false
38588                 });
38589                 return res
38590         }
38591
38592         function pluck(obj, fn) {
38593                 if (isList(obj)) {
38594                         for (var i=0; i<obj.length; i++) {
38595                                 if (fn(obj[i], i)) {
38596                                         return obj[i]
38597                                 }
38598                         }
38599                 } else {
38600                         for (var key in obj) {
38601                                 if (obj.hasOwnProperty(key)) {
38602                                         if (fn(obj[key], key)) {
38603                                                 return obj[key]
38604                                         }
38605                                 }
38606                         }
38607                 }
38608         }
38609
38610         function isList(val) {
38611                 return (val != null && typeof val != 'function' && typeof val.length == 'number')
38612         }
38613
38614         function isFunction$2(val) {
38615                 return val && {}.toString.call(val) === '[object Function]'
38616         }
38617
38618         function isObject$2(val) {
38619                 return val && {}.toString.call(val) === '[object Object]'
38620         }
38621
38622         var slice$6 = util.slice;
38623         var pluck$1 = util.pluck;
38624         var each$1 = util.each;
38625         var bind$4 = util.bind;
38626         var create$9 = util.create;
38627         var isList$1 = util.isList;
38628         var isFunction$3 = util.isFunction;
38629         var isObject$3 = util.isObject;
38630
38631         var storeEngine = {
38632                 createStore: createStore
38633         };
38634
38635         var storeAPI = {
38636                 version: '2.0.12',
38637                 enabled: false,
38638                 
38639                 // get returns the value of the given key. If that value
38640                 // is undefined, it returns optionalDefaultValue instead.
38641                 get: function(key, optionalDefaultValue) {
38642                         var data = this.storage.read(this._namespacePrefix + key);
38643                         return this._deserialize(data, optionalDefaultValue)
38644                 },
38645
38646                 // set will store the given value at key and returns value.
38647                 // Calling set with value === undefined is equivalent to calling remove.
38648                 set: function(key, value) {
38649                         if (value === undefined) {
38650                                 return this.remove(key)
38651                         }
38652                         this.storage.write(this._namespacePrefix + key, this._serialize(value));
38653                         return value
38654                 },
38655
38656                 // remove deletes the key and value stored at the given key.
38657                 remove: function(key) {
38658                         this.storage.remove(this._namespacePrefix + key);
38659                 },
38660
38661                 // each will call the given callback once for each key-value pair
38662                 // in this store.
38663                 each: function(callback) {
38664                         var self = this;
38665                         this.storage.each(function(val, namespacedKey) {
38666                                 callback.call(self, self._deserialize(val), (namespacedKey || '').replace(self._namespaceRegexp, ''));
38667                         });
38668                 },
38669
38670                 // clearAll will remove all the stored key-value pairs in this store.
38671                 clearAll: function() {
38672                         this.storage.clearAll();
38673                 },
38674
38675                 // additional functionality that can't live in plugins
38676                 // ---------------------------------------------------
38677
38678                 // hasNamespace returns true if this store instance has the given namespace.
38679                 hasNamespace: function(namespace) {
38680                         return (this._namespacePrefix == '__storejs_'+namespace+'_')
38681                 },
38682
38683                 // createStore creates a store.js instance with the first
38684                 // functioning storage in the list of storage candidates,
38685                 // and applies the the given mixins to the instance.
38686                 createStore: function() {
38687                         return createStore.apply(this, arguments)
38688                 },
38689                 
38690                 addPlugin: function(plugin) {
38691                         this._addPlugin(plugin);
38692                 },
38693                 
38694                 namespace: function(namespace) {
38695                         return createStore(this.storage, this.plugins, namespace)
38696                 }
38697         };
38698
38699         function _warn() {
38700                 var _console = (typeof console == 'undefined' ? null : console);
38701                 if (!_console) { return }
38702                 var fn = (_console.warn ? _console.warn : _console.log);
38703                 fn.apply(_console, arguments);
38704         }
38705
38706         function createStore(storages, plugins, namespace) {
38707                 if (!namespace) {
38708                         namespace = '';
38709                 }
38710                 if (storages && !isList$1(storages)) {
38711                         storages = [storages];
38712                 }
38713                 if (plugins && !isList$1(plugins)) {
38714                         plugins = [plugins];
38715                 }
38716
38717                 var namespacePrefix = (namespace ? '__storejs_'+namespace+'_' : '');
38718                 var namespaceRegexp = (namespace ? new RegExp('^'+namespacePrefix) : null);
38719                 var legalNamespaces = /^[a-zA-Z0-9_\-]*$/; // alpha-numeric + underscore and dash
38720                 if (!legalNamespaces.test(namespace)) {
38721                         throw new Error('store.js namespaces can only have alphanumerics + underscores and dashes')
38722                 }
38723                 
38724                 var _privateStoreProps = {
38725                         _namespacePrefix: namespacePrefix,
38726                         _namespaceRegexp: namespaceRegexp,
38727
38728                         _testStorage: function(storage) {
38729                                 try {
38730                                         var testStr = '__storejs__test__';
38731                                         storage.write(testStr, testStr);
38732                                         var ok = (storage.read(testStr) === testStr);
38733                                         storage.remove(testStr);
38734                                         return ok
38735                                 } catch(e) {
38736                                         return false
38737                                 }
38738                         },
38739
38740                         _assignPluginFnProp: function(pluginFnProp, propName) {
38741                                 var oldFn = this[propName];
38742                                 this[propName] = function pluginFn() {
38743                                         var args = slice$6(arguments, 0);
38744                                         var self = this;
38745
38746                                         // super_fn calls the old function which was overwritten by
38747                                         // this mixin.
38748                                         function super_fn() {
38749                                                 if (!oldFn) { return }
38750                                                 each$1(arguments, function(arg, i) {
38751                                                         args[i] = arg;
38752                                                 });
38753                                                 return oldFn.apply(self, args)
38754                                         }
38755
38756                                         // Give mixing function access to super_fn by prefixing all mixin function
38757                                         // arguments with super_fn.
38758                                         var newFnArgs = [super_fn].concat(args);
38759
38760                                         return pluginFnProp.apply(self, newFnArgs)
38761                                 };
38762                         },
38763
38764                         _serialize: function(obj) {
38765                                 return JSON.stringify(obj)
38766                         },
38767
38768                         _deserialize: function(strVal, defaultVal) {
38769                                 if (!strVal) { return defaultVal }
38770                                 // It is possible that a raw string value has been previously stored
38771                                 // in a storage without using store.js, meaning it will be a raw
38772                                 // string value instead of a JSON serialized string. By defaulting
38773                                 // to the raw string value in case of a JSON parse error, we allow
38774                                 // for past stored values to be forwards-compatible with store.js
38775                                 var val = '';
38776                                 try { val = JSON.parse(strVal); }
38777                                 catch(e) { val = strVal; }
38778
38779                                 return (val !== undefined ? val : defaultVal)
38780                         },
38781                         
38782                         _addStorage: function(storage) {
38783                                 if (this.enabled) { return }
38784                                 if (this._testStorage(storage)) {
38785                                         this.storage = storage;
38786                                         this.enabled = true;
38787                                 }
38788                         },
38789
38790                         _addPlugin: function(plugin) {
38791                                 var self = this;
38792
38793                                 // If the plugin is an array, then add all plugins in the array.
38794                                 // This allows for a plugin to depend on other plugins.
38795                                 if (isList$1(plugin)) {
38796                                         each$1(plugin, function(plugin) {
38797                                                 self._addPlugin(plugin);
38798                                         });
38799                                         return
38800                                 }
38801
38802                                 // Keep track of all plugins we've seen so far, so that we
38803                                 // don't add any of them twice.
38804                                 var seenPlugin = pluck$1(this.plugins, function(seenPlugin) {
38805                                         return (plugin === seenPlugin)
38806                                 });
38807                                 if (seenPlugin) {
38808                                         return
38809                                 }
38810                                 this.plugins.push(plugin);
38811
38812                                 // Check that the plugin is properly formed
38813                                 if (!isFunction$3(plugin)) {
38814                                         throw new Error('Plugins must be function values that return objects')
38815                                 }
38816
38817                                 var pluginProperties = plugin.call(this);
38818                                 if (!isObject$3(pluginProperties)) {
38819                                         throw new Error('Plugins must return an object of function properties')
38820                                 }
38821
38822                                 // Add the plugin function properties to this store instance.
38823                                 each$1(pluginProperties, function(pluginFnProp, propName) {
38824                                         if (!isFunction$3(pluginFnProp)) {
38825                                                 throw new Error('Bad plugin property: '+propName+' from plugin '+plugin.name+'. Plugins should only return functions.')
38826                                         }
38827                                         self._assignPluginFnProp(pluginFnProp, propName);
38828                                 });
38829                         },
38830                         
38831                         // Put deprecated properties in the private API, so as to not expose it to accidential
38832                         // discovery through inspection of the store object.
38833                         
38834                         // Deprecated: addStorage
38835                         addStorage: function(storage) {
38836                                 _warn('store.addStorage(storage) is deprecated. Use createStore([storages])');
38837                                 this._addStorage(storage);
38838                         }
38839                 };
38840
38841                 var store = create$9(_privateStoreProps, storeAPI, {
38842                         plugins: []
38843                 });
38844                 store.raw = {};
38845                 each$1(store, function(prop, propName) {
38846                         if (isFunction$3(prop)) {
38847                                 store.raw[propName] = bind$4(store, prop);                      
38848                         }
38849                 });
38850                 each$1(storages, function(storage) {
38851                         store._addStorage(storage);
38852                 });
38853                 each$1(plugins, function(plugin) {
38854                         store._addPlugin(plugin);
38855                 });
38856                 return store
38857         }
38858
38859         var Global$1 = util.Global;
38860
38861         var localStorage_1 = {
38862                 name: 'localStorage',
38863                 read: read,
38864                 write: write,
38865                 each: each$2,
38866                 remove: remove$2,
38867                 clearAll: clearAll,
38868         };
38869
38870         function localStorage$1() {
38871                 return Global$1.localStorage
38872         }
38873
38874         function read(key) {
38875                 return localStorage$1().getItem(key)
38876         }
38877
38878         function write(key, data) {
38879                 return localStorage$1().setItem(key, data)
38880         }
38881
38882         function each$2(fn) {
38883                 for (var i = localStorage$1().length - 1; i >= 0; i--) {
38884                         var key = localStorage$1().key(i);
38885                         fn(read(key), key);
38886                 }
38887         }
38888
38889         function remove$2(key) {
38890                 return localStorage$1().removeItem(key)
38891         }
38892
38893         function clearAll() {
38894                 return localStorage$1().clear()
38895         }
38896
38897         // oldFF-globalStorage provides storage for Firefox
38898         // versions 6 and 7, where no localStorage, etc
38899         // is available.
38900
38901
38902         var Global$2 = util.Global;
38903
38904         var oldFFGlobalStorage = {
38905                 name: 'oldFF-globalStorage',
38906                 read: read$1,
38907                 write: write$1,
38908                 each: each$3,
38909                 remove: remove$3,
38910                 clearAll: clearAll$1,
38911         };
38912
38913         var globalStorage = Global$2.globalStorage;
38914
38915         function read$1(key) {
38916                 return globalStorage[key]
38917         }
38918
38919         function write$1(key, data) {
38920                 globalStorage[key] = data;
38921         }
38922
38923         function each$3(fn) {
38924                 for (var i = globalStorage.length - 1; i >= 0; i--) {
38925                         var key = globalStorage.key(i);
38926                         fn(globalStorage[key], key);
38927                 }
38928         }
38929
38930         function remove$3(key) {
38931                 return globalStorage.removeItem(key)
38932         }
38933
38934         function clearAll$1() {
38935                 each$3(function(key, _) {
38936                         delete globalStorage[key];
38937                 });
38938         }
38939
38940         // oldIE-userDataStorage provides storage for Internet Explorer
38941         // versions 6 and 7, where no localStorage, sessionStorage, etc
38942         // is available.
38943
38944
38945         var Global$3 = util.Global;
38946
38947         var oldIEUserDataStorage = {
38948                 name: 'oldIE-userDataStorage',
38949                 write: write$2,
38950                 read: read$2,
38951                 each: each$4,
38952                 remove: remove$4,
38953                 clearAll: clearAll$2,
38954         };
38955
38956         var storageName = 'storejs';
38957         var doc = Global$3.document;
38958         var _withStorageEl = _makeIEStorageElFunction();
38959         var disable = (Global$3.navigator ? Global$3.navigator.userAgent : '').match(/ (MSIE 8|MSIE 9|MSIE 10)\./); // MSIE 9.x, MSIE 10.x
38960
38961         function write$2(unfixedKey, data) {
38962                 if (disable) { return }
38963                 var fixedKey = fixKey(unfixedKey);
38964                 _withStorageEl(function(storageEl) {
38965                         storageEl.setAttribute(fixedKey, data);
38966                         storageEl.save(storageName);
38967                 });
38968         }
38969
38970         function read$2(unfixedKey) {
38971                 if (disable) { return }
38972                 var fixedKey = fixKey(unfixedKey);
38973                 var res = null;
38974                 _withStorageEl(function(storageEl) {
38975                         res = storageEl.getAttribute(fixedKey);
38976                 });
38977                 return res
38978         }
38979
38980         function each$4(callback) {
38981                 _withStorageEl(function(storageEl) {
38982                         var attributes = storageEl.XMLDocument.documentElement.attributes;
38983                         for (var i=attributes.length-1; i>=0; i--) {
38984                                 var attr = attributes[i];
38985                                 callback(storageEl.getAttribute(attr.name), attr.name);
38986                         }
38987                 });
38988         }
38989
38990         function remove$4(unfixedKey) {
38991                 var fixedKey = fixKey(unfixedKey);
38992                 _withStorageEl(function(storageEl) {
38993                         storageEl.removeAttribute(fixedKey);
38994                         storageEl.save(storageName);
38995                 });
38996         }
38997
38998         function clearAll$2() {
38999                 _withStorageEl(function(storageEl) {
39000                         var attributes = storageEl.XMLDocument.documentElement.attributes;
39001                         storageEl.load(storageName);
39002                         for (var i=attributes.length-1; i>=0; i--) {
39003                                 storageEl.removeAttribute(attributes[i].name);
39004                         }
39005                         storageEl.save(storageName);
39006                 });
39007         }
39008
39009         // Helpers
39010         //////////
39011
39012         // In IE7, keys cannot start with a digit or contain certain chars.
39013         // See https://github.com/marcuswestin/store.js/issues/40
39014         // See https://github.com/marcuswestin/store.js/issues/83
39015         var forbiddenCharsRegex = new RegExp("[!\"#$%&'()*+,/\\\\:;<=>?@[\\]^`{|}~]", "g");
39016         function fixKey(key) {
39017                 return key.replace(/^\d/, '___$&').replace(forbiddenCharsRegex, '___')
39018         }
39019
39020         function _makeIEStorageElFunction() {
39021                 if (!doc || !doc.documentElement || !doc.documentElement.addBehavior) {
39022                         return null
39023                 }
39024                 var scriptTag = 'script',
39025                         storageOwner,
39026                         storageContainer,
39027                         storageEl;
39028
39029                 // Since #userData storage applies only to specific paths, we need to
39030                 // somehow link our data to a specific path.  We choose /favicon.ico
39031                 // as a pretty safe option, since all browsers already make a request to
39032                 // this URL anyway and being a 404 will not hurt us here.  We wrap an
39033                 // iframe pointing to the favicon in an ActiveXObject(htmlfile) object
39034                 // (see: http://msdn.microsoft.com/en-us/library/aa752574(v=VS.85).aspx)
39035                 // since the iframe access rules appear to allow direct access and
39036                 // manipulation of the document element, even for a 404 page.  This
39037                 // document can be used instead of the current document (which would
39038                 // have been limited to the current path) to perform #userData storage.
39039                 try {
39040                         /* global ActiveXObject */
39041                         storageContainer = new ActiveXObject('htmlfile');
39042                         storageContainer.open();
39043                         storageContainer.write('<'+scriptTag+'>document.w=window</'+scriptTag+'><iframe src="/favicon.ico"></iframe>');
39044                         storageContainer.close();
39045                         storageOwner = storageContainer.w.frames[0].document;
39046                         storageEl = storageOwner.createElement('div');
39047                 } catch(e) {
39048                         // somehow ActiveXObject instantiation failed (perhaps some special
39049                         // security settings or otherwse), fall back to per-path storage
39050                         storageEl = doc.createElement('div');
39051                         storageOwner = doc.body;
39052                 }
39053
39054                 return function(storeFunction) {
39055                         var args = [].slice.call(arguments, 0);
39056                         args.unshift(storageEl);
39057                         // See http://msdn.microsoft.com/en-us/library/ms531081(v=VS.85).aspx
39058                         // and http://msdn.microsoft.com/en-us/library/ms531424(v=VS.85).aspx
39059                         storageOwner.appendChild(storageEl);
39060                         storageEl.addBehavior('#default#userData');
39061                         storageEl.load(storageName);
39062                         storeFunction.apply(this, args);
39063                         storageOwner.removeChild(storageEl);
39064                         return
39065                 }
39066         }
39067
39068         // cookieStorage is useful Safari private browser mode, where localStorage
39069         // doesn't work but cookies do. This implementation is adopted from
39070         // https://developer.mozilla.org/en-US/docs/Web/API/Storage/LocalStorage
39071
39072
39073         var Global$4 = util.Global;
39074         var trim$1 = util.trim;
39075
39076         var cookieStorage = {
39077                 name: 'cookieStorage',
39078                 read: read$3,
39079                 write: write$3,
39080                 each: each$5,
39081                 remove: remove$5,
39082                 clearAll: clearAll$3,
39083         };
39084
39085         var doc$1 = Global$4.document;
39086
39087         function read$3(key) {
39088                 if (!key || !_has(key)) { return null }
39089                 var regexpStr = "(?:^|.*;\\s*)" +
39090                         escape(key).replace(/[\-\.\+\*]/g, "\\$&") +
39091                         "\\s*\\=\\s*((?:[^;](?!;))*[^;]?).*";
39092                 return unescape(doc$1.cookie.replace(new RegExp(regexpStr), "$1"))
39093         }
39094
39095         function each$5(callback) {
39096                 var cookies = doc$1.cookie.split(/; ?/g);
39097                 for (var i = cookies.length - 1; i >= 0; i--) {
39098                         if (!trim$1(cookies[i])) {
39099                                 continue
39100                         }
39101                         var kvp = cookies[i].split('=');
39102                         var key = unescape(kvp[0]);
39103                         var val = unescape(kvp[1]);
39104                         callback(val, key);
39105                 }
39106         }
39107
39108         function write$3(key, data) {
39109                 if(!key) { return }
39110                 doc$1.cookie = escape(key) + "=" + escape(data) + "; expires=Tue, 19 Jan 2038 03:14:07 GMT; path=/";
39111         }
39112
39113         function remove$5(key) {
39114                 if (!key || !_has(key)) {
39115                         return
39116                 }
39117                 doc$1.cookie = escape(key) + "=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/";
39118         }
39119
39120         function clearAll$3() {
39121                 each$5(function(_, key) {
39122                         remove$5(key);
39123                 });
39124         }
39125
39126         function _has(key) {
39127                 return (new RegExp("(?:^|;\\s*)" + escape(key).replace(/[\-\.\+\*]/g, "\\$&") + "\\s*\\=")).test(doc$1.cookie)
39128         }
39129
39130         var Global$5 = util.Global;
39131
39132         var sessionStorage_1 = {
39133                 name: 'sessionStorage',
39134                 read: read$4,
39135                 write: write$4,
39136                 each: each$6,
39137                 remove: remove$6,
39138                 clearAll: clearAll$4
39139         };
39140
39141         function sessionStorage() {
39142                 return Global$5.sessionStorage
39143         }
39144
39145         function read$4(key) {
39146                 return sessionStorage().getItem(key)
39147         }
39148
39149         function write$4(key, data) {
39150                 return sessionStorage().setItem(key, data)
39151         }
39152
39153         function each$6(fn) {
39154                 for (var i = sessionStorage().length - 1; i >= 0; i--) {
39155                         var key = sessionStorage().key(i);
39156                         fn(read$4(key), key);
39157                 }
39158         }
39159
39160         function remove$6(key) {
39161                 return sessionStorage().removeItem(key)
39162         }
39163
39164         function clearAll$4() {
39165                 return sessionStorage().clear()
39166         }
39167
39168         // memoryStorage is a useful last fallback to ensure that the store
39169         // is functions (meaning store.get(), store.set(), etc will all function).
39170         // However, stored values will not persist when the browser navigates to
39171         // a new page or reloads the current page.
39172
39173         var memoryStorage_1 = {
39174                 name: 'memoryStorage',
39175                 read: read$5,
39176                 write: write$5,
39177                 each: each$7,
39178                 remove: remove$7,
39179                 clearAll: clearAll$5,
39180         };
39181
39182         var memoryStorage = {};
39183
39184         function read$5(key) {
39185                 return memoryStorage[key]
39186         }
39187
39188         function write$5(key, data) {
39189                 memoryStorage[key] = data;
39190         }
39191
39192         function each$7(callback) {
39193                 for (var key in memoryStorage) {
39194                         if (memoryStorage.hasOwnProperty(key)) {
39195                                 callback(memoryStorage[key], key);
39196                         }
39197                 }
39198         }
39199
39200         function remove$7(key) {
39201                 delete memoryStorage[key];
39202         }
39203
39204         function clearAll$5(key) {
39205                 memoryStorage = {};
39206         }
39207
39208         var all = [
39209                 // Listed in order of usage preference
39210                 localStorage_1,
39211                 oldFFGlobalStorage,
39212                 oldIEUserDataStorage,
39213                 cookieStorage,
39214                 sessionStorage_1,
39215                 memoryStorage_1
39216         ];
39217
39218         /* eslint-disable */
39219
39220         //  json2.js
39221         //  2016-10-28
39222         //  Public Domain.
39223         //  NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
39224         //  See http://www.JSON.org/js.html
39225         //  This code should be minified before deployment.
39226         //  See http://javascript.crockford.com/jsmin.html
39227
39228         //  USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
39229         //  NOT CONTROL.
39230
39231         //  This file creates a global JSON object containing two methods: stringify
39232         //  and parse. This file provides the ES5 JSON capability to ES3 systems.
39233         //  If a project might run on IE8 or earlier, then this file should be included.
39234         //  This file does nothing on ES5 systems.
39235
39236         //      JSON.stringify(value, replacer, space)
39237         //          value       any JavaScript value, usually an object or array.
39238         //          replacer    an optional parameter that determines how object
39239         //                      values are stringified for objects. It can be a
39240         //                      function or an array of strings.
39241         //          space       an optional parameter that specifies the indentation
39242         //                      of nested structures. If it is omitted, the text will
39243         //                      be packed without extra whitespace. If it is a number,
39244         //                      it will specify the number of spaces to indent at each
39245         //                      level. If it is a string (such as "\t" or "&nbsp;"),
39246         //                      it contains the characters used to indent at each level.
39247         //          This method produces a JSON text from a JavaScript value.
39248         //          When an object value is found, if the object contains a toJSON
39249         //          method, its toJSON method will be called and the result will be
39250         //          stringified. A toJSON method does not serialize: it returns the
39251         //          value represented by the name/value pair that should be serialized,
39252         //          or undefined if nothing should be serialized. The toJSON method
39253         //          will be passed the key associated with the value, and this will be
39254         //          bound to the value.
39255
39256         //          For example, this would serialize Dates as ISO strings.
39257
39258         //              Date.prototype.toJSON = function (key) {
39259         //                  function f(n) {
39260         //                      // Format integers to have at least two digits.
39261         //                      return (n < 10)
39262         //                          ? "0" + n
39263         //                          : n;
39264         //                  }
39265         //                  return this.getUTCFullYear()   + "-" +
39266         //                       f(this.getUTCMonth() + 1) + "-" +
39267         //                       f(this.getUTCDate())      + "T" +
39268         //                       f(this.getUTCHours())     + ":" +
39269         //                       f(this.getUTCMinutes())   + ":" +
39270         //                       f(this.getUTCSeconds())   + "Z";
39271         //              };
39272
39273         //          You can provide an optional replacer method. It will be passed the
39274         //          key and value of each member, with this bound to the containing
39275         //          object. The value that is returned from your method will be
39276         //          serialized. If your method returns undefined, then the member will
39277         //          be excluded from the serialization.
39278
39279         //          If the replacer parameter is an array of strings, then it will be
39280         //          used to select the members to be serialized. It filters the results
39281         //          such that only members with keys listed in the replacer array are
39282         //          stringified.
39283
39284         //          Values that do not have JSON representations, such as undefined or
39285         //          functions, will not be serialized. Such values in objects will be
39286         //          dropped; in arrays they will be replaced with null. You can use
39287         //          a replacer function to replace those with JSON values.
39288
39289         //          JSON.stringify(undefined) returns undefined.
39290
39291         //          The optional space parameter produces a stringification of the
39292         //          value that is filled with line breaks and indentation to make it
39293         //          easier to read.
39294
39295         //          If the space parameter is a non-empty string, then that string will
39296         //          be used for indentation. If the space parameter is a number, then
39297         //          the indentation will be that many spaces.
39298
39299         //          Example:
39300
39301         //          text = JSON.stringify(["e", {pluribus: "unum"}]);
39302         //          // text is '["e",{"pluribus":"unum"}]'
39303
39304         //          text = JSON.stringify(["e", {pluribus: "unum"}], null, "\t");
39305         //          // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'
39306
39307         //          text = JSON.stringify([new Date()], function (key, value) {
39308         //              return this[key] instanceof Date
39309         //                  ? "Date(" + this[key] + ")"
39310         //                  : value;
39311         //          });
39312         //          // text is '["Date(---current time---)"]'
39313
39314         //      JSON.parse(text, reviver)
39315         //          This method parses a JSON text to produce an object or array.
39316         //          It can throw a SyntaxError exception.
39317
39318         //          The optional reviver parameter is a function that can filter and
39319         //          transform the results. It receives each of the keys and values,
39320         //          and its return value is used instead of the original value.
39321         //          If it returns what it received, then the structure is not modified.
39322         //          If it returns undefined then the member is deleted.
39323
39324         //          Example:
39325
39326         //          // Parse the text. Values that look like ISO date strings will
39327         //          // be converted to Date objects.
39328
39329         //          myData = JSON.parse(text, function (key, value) {
39330         //              var a;
39331         //              if (typeof value === "string") {
39332         //                  a =
39333         //   /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
39334         //                  if (a) {
39335         //                      return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
39336         //                          +a[5], +a[6]));
39337         //                  }
39338         //              }
39339         //              return value;
39340         //          });
39341
39342         //          myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) {
39343         //              var d;
39344         //              if (typeof value === "string" &&
39345         //                      value.slice(0, 5) === "Date(" &&
39346         //                      value.slice(-1) === ")") {
39347         //                  d = new Date(value.slice(5, -1));
39348         //                  if (d) {
39349         //                      return d;
39350         //                  }
39351         //              }
39352         //              return value;
39353         //          });
39354
39355         //  This is a reference implementation. You are free to copy, modify, or
39356         //  redistribute.
39357
39358         /*jslint
39359             eval, for, this
39360         */
39361
39362         /*property
39363             JSON, apply, call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
39364             getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,
39365             lastIndex, length, parse, prototype, push, replace, slice, stringify,
39366             test, toJSON, toString, valueOf
39367         */
39368
39369
39370         // Create a JSON object only if one does not already exist. We create the
39371         // methods in a closure to avoid creating global variables.
39372
39373         if (typeof JSON !== "object") {
39374             JSON = {};
39375         }
39376
39377         (function () {
39378
39379             var rx_one = /^[\],:{}\s]*$/;
39380             var rx_two = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g;
39381             var rx_three = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g;
39382             var rx_four = /(?:^|:|,)(?:\s*\[)+/g;
39383             var rx_escapable = /[\\"\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
39384             var rx_dangerous = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
39385
39386             function f(n) {
39387                 // Format integers to have at least two digits.
39388                 return n < 10
39389                     ? "0" + n
39390                     : n;
39391             }
39392
39393             function this_value() {
39394                 return this.valueOf();
39395             }
39396
39397             if (typeof Date.prototype.toJSON !== "function") {
39398
39399                 Date.prototype.toJSON = function () {
39400
39401                     return isFinite(this.valueOf())
39402                         ? this.getUTCFullYear() + "-" +
39403                                 f(this.getUTCMonth() + 1) + "-" +
39404                                 f(this.getUTCDate()) + "T" +
39405                                 f(this.getUTCHours()) + ":" +
39406                                 f(this.getUTCMinutes()) + ":" +
39407                                 f(this.getUTCSeconds()) + "Z"
39408                         : null;
39409                 };
39410
39411                 Boolean.prototype.toJSON = this_value;
39412                 Number.prototype.toJSON = this_value;
39413                 String.prototype.toJSON = this_value;
39414             }
39415
39416             var gap;
39417             var indent;
39418             var meta;
39419             var rep;
39420
39421
39422             function quote(string) {
39423
39424         // If the string contains no control characters, no quote characters, and no
39425         // backslash characters, then we can safely slap some quotes around it.
39426         // Otherwise we must also replace the offending characters with safe escape
39427         // sequences.
39428
39429                 rx_escapable.lastIndex = 0;
39430                 return rx_escapable.test(string)
39431                     ? "\"" + string.replace(rx_escapable, function (a) {
39432                         var c = meta[a];
39433                         return typeof c === "string"
39434                             ? c
39435                             : "\\u" + ("0000" + a.charCodeAt(0).toString(16)).slice(-4);
39436                     }) + "\""
39437                     : "\"" + string + "\"";
39438             }
39439
39440
39441             function str(key, holder) {
39442
39443         // Produce a string from holder[key].
39444
39445                 var i;          // The loop counter.
39446                 var k;          // The member key.
39447                 var v;          // The member value.
39448                 var length;
39449                 var mind = gap;
39450                 var partial;
39451                 var value = holder[key];
39452
39453         // If the value has a toJSON method, call it to obtain a replacement value.
39454
39455                 if (value && typeof value === "object" &&
39456                         typeof value.toJSON === "function") {
39457                     value = value.toJSON(key);
39458                 }
39459
39460         // If we were called with a replacer function, then call the replacer to
39461         // obtain a replacement value.
39462
39463                 if (typeof rep === "function") {
39464                     value = rep.call(holder, key, value);
39465                 }
39466
39467         // What happens next depends on the value's type.
39468
39469                 switch (typeof value) {
39470                 case "string":
39471                     return quote(value);
39472
39473                 case "number":
39474
39475         // JSON numbers must be finite. Encode non-finite numbers as null.
39476
39477                     return isFinite(value)
39478                         ? String(value)
39479                         : "null";
39480
39481                 case "boolean":
39482                 case "null":
39483
39484         // If the value is a boolean or null, convert it to a string. Note:
39485         // typeof null does not produce "null". The case is included here in
39486         // the remote chance that this gets fixed someday.
39487
39488                     return String(value);
39489
39490         // If the type is "object", we might be dealing with an object or an array or
39491         // null.
39492
39493                 case "object":
39494
39495         // Due to a specification blunder in ECMAScript, typeof null is "object",
39496         // so watch out for that case.
39497
39498                     if (!value) {
39499                         return "null";
39500                     }
39501
39502         // Make an array to hold the partial results of stringifying this object value.
39503
39504                     gap += indent;
39505                     partial = [];
39506
39507         // Is the value an array?
39508
39509                     if (Object.prototype.toString.apply(value) === "[object Array]") {
39510
39511         // The value is an array. Stringify every element. Use null as a placeholder
39512         // for non-JSON values.
39513
39514                         length = value.length;
39515                         for (i = 0; i < length; i += 1) {
39516                             partial[i] = str(i, value) || "null";
39517                         }
39518
39519         // Join all of the elements together, separated with commas, and wrap them in
39520         // brackets.
39521
39522                         v = partial.length === 0
39523                             ? "[]"
39524                             : gap
39525                                 ? "[\n" + gap + partial.join(",\n" + gap) + "\n" + mind + "]"
39526                                 : "[" + partial.join(",") + "]";
39527                         gap = mind;
39528                         return v;
39529                     }
39530
39531         // If the replacer is an array, use it to select the members to be stringified.
39532
39533                     if (rep && typeof rep === "object") {
39534                         length = rep.length;
39535                         for (i = 0; i < length; i += 1) {
39536                             if (typeof rep[i] === "string") {
39537                                 k = rep[i];
39538                                 v = str(k, value);
39539                                 if (v) {
39540                                     partial.push(quote(k) + (
39541                                         gap
39542                                             ? ": "
39543                                             : ":"
39544                                     ) + v);
39545                                 }
39546                             }
39547                         }
39548                     } else {
39549
39550         // Otherwise, iterate through all of the keys in the object.
39551
39552                         for (k in value) {
39553                             if (Object.prototype.hasOwnProperty.call(value, k)) {
39554                                 v = str(k, value);
39555                                 if (v) {
39556                                     partial.push(quote(k) + (
39557                                         gap
39558                                             ? ": "
39559                                             : ":"
39560                                     ) + v);
39561                                 }
39562                             }
39563                         }
39564                     }
39565
39566         // Join all of the member texts together, separated with commas,
39567         // and wrap them in braces.
39568
39569                     v = partial.length === 0
39570                         ? "{}"
39571                         : gap
39572                             ? "{\n" + gap + partial.join(",\n" + gap) + "\n" + mind + "}"
39573                             : "{" + partial.join(",") + "}";
39574                     gap = mind;
39575                     return v;
39576                 }
39577             }
39578
39579         // If the JSON object does not yet have a stringify method, give it one.
39580
39581             if (typeof JSON.stringify !== "function") {
39582                 meta = {    // table of character substitutions
39583                     "\b": "\\b",
39584                     "\t": "\\t",
39585                     "\n": "\\n",
39586                     "\f": "\\f",
39587                     "\r": "\\r",
39588                     "\"": "\\\"",
39589                     "\\": "\\\\"
39590                 };
39591                 JSON.stringify = function (value, replacer, space) {
39592
39593         // The stringify method takes a value and an optional replacer, and an optional
39594         // space parameter, and returns a JSON text. The replacer can be a function
39595         // that can replace values, or an array of strings that will select the keys.
39596         // A default replacer method can be provided. Use of the space parameter can
39597         // produce text that is more easily readable.
39598
39599                     var i;
39600                     gap = "";
39601                     indent = "";
39602
39603         // If the space parameter is a number, make an indent string containing that
39604         // many spaces.
39605
39606                     if (typeof space === "number") {
39607                         for (i = 0; i < space; i += 1) {
39608                             indent += " ";
39609                         }
39610
39611         // If the space parameter is a string, it will be used as the indent string.
39612
39613                     } else if (typeof space === "string") {
39614                         indent = space;
39615                     }
39616
39617         // If there is a replacer, it must be a function or an array.
39618         // Otherwise, throw an error.
39619
39620                     rep = replacer;
39621                     if (replacer && typeof replacer !== "function" &&
39622                             (typeof replacer !== "object" ||
39623                             typeof replacer.length !== "number")) {
39624                         throw new Error("JSON.stringify");
39625                     }
39626
39627         // Make a fake root object containing our value under the key of "".
39628         // Return the result of stringifying the value.
39629
39630                     return str("", {"": value});
39631                 };
39632             }
39633
39634
39635         // If the JSON object does not yet have a parse method, give it one.
39636
39637             if (typeof JSON.parse !== "function") {
39638                 JSON.parse = function (text, reviver) {
39639
39640         // The parse method takes a text and an optional reviver function, and returns
39641         // a JavaScript value if the text is a valid JSON text.
39642
39643                     var j;
39644
39645                     function walk(holder, key) {
39646
39647         // The walk method is used to recursively walk the resulting structure so
39648         // that modifications can be made.
39649
39650                         var k;
39651                         var v;
39652                         var value = holder[key];
39653                         if (value && typeof value === "object") {
39654                             for (k in value) {
39655                                 if (Object.prototype.hasOwnProperty.call(value, k)) {
39656                                     v = walk(value, k);
39657                                     if (v !== undefined) {
39658                                         value[k] = v;
39659                                     } else {
39660                                         delete value[k];
39661                                     }
39662                                 }
39663                             }
39664                         }
39665                         return reviver.call(holder, key, value);
39666                     }
39667
39668
39669         // Parsing happens in four stages. In the first stage, we replace certain
39670         // Unicode characters with escape sequences. JavaScript handles many characters
39671         // incorrectly, either silently deleting them, or treating them as line endings.
39672
39673                     text = String(text);
39674                     rx_dangerous.lastIndex = 0;
39675                     if (rx_dangerous.test(text)) {
39676                         text = text.replace(rx_dangerous, function (a) {
39677                             return "\\u" +
39678                                     ("0000" + a.charCodeAt(0).toString(16)).slice(-4);
39679                         });
39680                     }
39681
39682         // In the second stage, we run the text against regular expressions that look
39683         // for non-JSON patterns. We are especially concerned with "()" and "new"
39684         // because they can cause invocation, and "=" because it can cause mutation.
39685         // But just to be safe, we want to reject all unexpected forms.
39686
39687         // We split the second stage into 4 regexp operations in order to work around
39688         // crippling inefficiencies in IE's and Safari's regexp engines. First we
39689         // replace the JSON backslash pairs with "@" (a non-JSON character). Second, we
39690         // replace all simple value tokens with "]" characters. Third, we delete all
39691         // open brackets that follow a colon or comma or that begin the text. Finally,
39692         // we look to see that the remaining characters are only whitespace or "]" or
39693         // "," or ":" or "{" or "}". If that is so, then the text is safe for eval.
39694
39695                     if (
39696                         rx_one.test(
39697                             text
39698                                 .replace(rx_two, "@")
39699                                 .replace(rx_three, "]")
39700                                 .replace(rx_four, "")
39701                         )
39702                     ) {
39703
39704         // In the third stage we use the eval function to compile the text into a
39705         // JavaScript structure. The "{" operator is subject to a syntactic ambiguity
39706         // in JavaScript: it can begin a block or an object literal. We wrap the text
39707         // in parens to eliminate the ambiguity.
39708
39709                         j = eval("(" + text + ")");
39710
39711         // In the optional fourth stage, we recursively walk the new structure, passing
39712         // each name/value pair to a reviver function for possible transformation.
39713
39714                         return (typeof reviver === "function")
39715                             ? walk({"": j}, "")
39716                             : j;
39717                     }
39718
39719         // If the text is not JSON parseable, then a SyntaxError is thrown.
39720
39721                     throw new SyntaxError("JSON.parse");
39722                 };
39723             }
39724         }());
39725
39726         var json2 = json2Plugin;
39727
39728         function json2Plugin() {
39729                 
39730                 return {}
39731         }
39732
39733         var plugins = [json2];
39734
39735         var store_legacy = storeEngine.createStore(all, plugins);
39736
39737         // # osm-auth
39738         //
39739         // This code is only compatible with IE10+ because the [XDomainRequest](http://bit.ly/LfO7xo)
39740         // object, IE<10's idea of [CORS](http://en.wikipedia.org/wiki/Cross-origin_resource_sharing),
39741         // does not support custom headers, which this uses everywhere.
39742         var osmAuth = function(o) {
39743
39744             var oauth = {};
39745
39746             // authenticated users will also have a request token secret, but it's
39747             // not used in transactions with the server
39748             oauth.authenticated = function() {
39749                 return !!(token('oauth_token') && token('oauth_token_secret'));
39750             };
39751
39752             oauth.logout = function() {
39753                 token('oauth_token', '');
39754                 token('oauth_token_secret', '');
39755                 token('oauth_request_token_secret', '');
39756                 return oauth;
39757             };
39758
39759             // TODO: detect lack of click event
39760             oauth.authenticate = function(callback) {
39761                 if (oauth.authenticated()) { return callback(); }
39762
39763                 oauth.logout();
39764
39765                 // ## Getting a request token
39766                 var params = timenonce(getAuth(o)),
39767                     url = o.url + '/oauth/request_token';
39768
39769                 params.oauth_signature = ohauth_1.signature(
39770                     o.oauth_secret, '',
39771                     ohauth_1.baseString('POST', url, params));
39772
39773                 if (!o.singlepage) {
39774                     // Create a 600x550 popup window in the center of the screen
39775                     var w = 600, h = 550,
39776                         settings = [
39777                             ['width', w], ['height', h],
39778                             ['left', screen.width / 2 - w / 2],
39779                             ['top', screen.height / 2 - h / 2]].map(function(x) {
39780                                 return x.join('=');
39781                             }).join(','),
39782                         popup = window.open('about:blank', 'oauth_window', settings);
39783
39784                     oauth.popupWindow = popup;
39785
39786                     if (!popup) {
39787                         var error = new Error('Popup was blocked');
39788                         error.status = 'popup-blocked';
39789                         throw error;
39790                     }
39791                 }
39792
39793                 // Request a request token. When this is complete, the popup
39794                 // window is redirected to OSM's authorization page.
39795                 ohauth_1.xhr('POST', url, params, null, {}, reqTokenDone);
39796                 o.loading();
39797
39798                 function reqTokenDone(err, xhr) {
39799                     o.done();
39800                     if (err) { return callback(err); }
39801                     var resp = ohauth_1.stringQs(xhr.response);
39802                     token('oauth_request_token_secret', resp.oauth_token_secret);
39803                     var authorize_url = o.url + '/oauth/authorize?' + ohauth_1.qsString({
39804                         oauth_token: resp.oauth_token,
39805                         oauth_callback: resolveUrl$1(o.landing)
39806                     });
39807
39808                     if (o.singlepage) {
39809                         location.href = authorize_url;
39810                     } else {
39811                         popup.location = authorize_url;
39812                     }
39813                 }
39814
39815                 // Called by a function in a landing page, in the popup window. The
39816                 // window closes itself.
39817                 window.authComplete = function(token) {
39818                     var oauth_token = ohauth_1.stringQs(token.split('?')[1]);
39819                     get_access_token(oauth_token.oauth_token);
39820                     delete window.authComplete;
39821                 };
39822
39823                 // ## Getting an request token
39824                 //
39825                 // At this point we have an `oauth_token`, brought in from a function
39826                 // call on a landing page popup.
39827                 function get_access_token(oauth_token) {
39828                     var url = o.url + '/oauth/access_token',
39829                         params = timenonce(getAuth(o)),
39830                         request_token_secret = token('oauth_request_token_secret');
39831                     params.oauth_token = oauth_token;
39832                     params.oauth_signature = ohauth_1.signature(
39833                         o.oauth_secret,
39834                         request_token_secret,
39835                         ohauth_1.baseString('POST', url, params));
39836
39837                     // ## Getting an access token
39838                     //
39839                     // The final token required for authentication. At this point
39840                     // we have a `request token secret`
39841                     ohauth_1.xhr('POST', url, params, null, {}, accessTokenDone);
39842                     o.loading();
39843                 }
39844
39845                 function accessTokenDone(err, xhr) {
39846                     o.done();
39847                     if (err) { return callback(err); }
39848                     var access_token = ohauth_1.stringQs(xhr.response);
39849                     token('oauth_token', access_token.oauth_token);
39850                     token('oauth_token_secret', access_token.oauth_token_secret);
39851                     callback(null, oauth);
39852                 }
39853             };
39854
39855             oauth.bringPopupWindowToFront = function() {
39856                 var brougtPopupToFront = false;
39857                 try {
39858                     // This may cause a cross-origin error:
39859                     // `DOMException: Blocked a frame with origin "..." from accessing a cross-origin frame.`
39860                     if (oauth.popupWindow && !oauth.popupWindow.closed) {
39861                         oauth.popupWindow.focus();
39862                         brougtPopupToFront = true;
39863                     }
39864                 } catch (err) {
39865                     // Bringing popup window to front failed (probably because of the cross-origin error mentioned above)
39866                 }
39867                 return brougtPopupToFront;
39868             };
39869
39870             oauth.bootstrapToken = function(oauth_token, callback) {
39871                 // ## Getting an request token
39872                 // At this point we have an `oauth_token`, brought in from a function
39873                 // call on a landing page popup.
39874                 function get_access_token(oauth_token) {
39875                     var url = o.url + '/oauth/access_token',
39876                         params = timenonce(getAuth(o)),
39877                         request_token_secret = token('oauth_request_token_secret');
39878                     params.oauth_token = oauth_token;
39879                     params.oauth_signature = ohauth_1.signature(
39880                         o.oauth_secret,
39881                         request_token_secret,
39882                         ohauth_1.baseString('POST', url, params));
39883
39884                     // ## Getting an access token
39885                     // The final token required for authentication. At this point
39886                     // we have a `request token secret`
39887                     ohauth_1.xhr('POST', url, params, null, {}, accessTokenDone);
39888                     o.loading();
39889                 }
39890
39891                 function accessTokenDone(err, xhr) {
39892                     o.done();
39893                     if (err) { return callback(err); }
39894                     var access_token = ohauth_1.stringQs(xhr.response);
39895                     token('oauth_token', access_token.oauth_token);
39896                     token('oauth_token_secret', access_token.oauth_token_secret);
39897                     callback(null, oauth);
39898                 }
39899
39900                 get_access_token(oauth_token);
39901             };
39902
39903             // # xhr
39904             //
39905             // A single XMLHttpRequest wrapper that does authenticated calls if the
39906             // user has logged in.
39907             oauth.xhr = function(options, callback) {
39908                 if (!oauth.authenticated()) {
39909                     if (o.auto) {
39910                         return oauth.authenticate(run);
39911                     } else {
39912                         callback('not authenticated', null);
39913                         return;
39914                     }
39915                 } else {
39916                     return run();
39917                 }
39918
39919                 function run() {
39920                     var params = timenonce(getAuth(o)),
39921                         oauth_token_secret = token('oauth_token_secret'),
39922                         url = (options.prefix !== false) ? o.url + options.path : options.path,
39923                         url_parts = url.replace(/#.*$/, '').split('?', 2),
39924                         base_url = url_parts[0],
39925                         query = (url_parts.length === 2) ? url_parts[1] : '';
39926
39927                     // https://tools.ietf.org/html/rfc5849#section-3.4.1.3.1
39928                     if ((!options.options || !options.options.header ||
39929                         options.options.header['Content-Type'] === 'application/x-www-form-urlencoded') &&
39930                         options.content) {
39931                         params = immutable(params, ohauth_1.stringQs(options.content));
39932                     }
39933
39934                     params.oauth_token = token('oauth_token');
39935                     params.oauth_signature = ohauth_1.signature(
39936                         o.oauth_secret,
39937                         oauth_token_secret,
39938                         ohauth_1.baseString(options.method, base_url, immutable(params, ohauth_1.stringQs(query)))
39939                     );
39940
39941                     return ohauth_1.xhr(options.method, url, params, options.content, options.options, done);
39942                 }
39943
39944                 function done(err, xhr) {
39945                     if (err) { return callback(err); }
39946                     else if (xhr.responseXML) { return callback(err, xhr.responseXML); }
39947                     else { return callback(err, xhr.response); }
39948                 }
39949             };
39950
39951             // pre-authorize this object, if we can just get a token and token_secret
39952             // from the start
39953             oauth.preauth = function(c) {
39954                 if (!c) { return; }
39955                 if (c.oauth_token) { token('oauth_token', c.oauth_token); }
39956                 if (c.oauth_token_secret) { token('oauth_token_secret', c.oauth_token_secret); }
39957                 return oauth;
39958             };
39959
39960             oauth.options = function(_) {
39961                 if (!arguments.length) { return o; }
39962
39963                 o = _;
39964                 o.url = o.url || 'https://www.openstreetmap.org';
39965                 o.landing = o.landing || 'land.html';
39966                 o.singlepage = o.singlepage || false;
39967
39968                 // Optional loading and loading-done functions for nice UI feedback.
39969                 // by default, no-ops
39970                 o.loading = o.loading || function() {};
39971                 o.done = o.done || function() {};
39972
39973                 return oauth.preauth(o);
39974             };
39975
39976             // 'stamp' an authentication object from `getAuth()`
39977             // with a [nonce](http://en.wikipedia.org/wiki/Cryptographic_nonce)
39978             // and timestamp
39979             function timenonce(o) {
39980                 o.oauth_timestamp = ohauth_1.timestamp();
39981                 o.oauth_nonce = ohauth_1.nonce();
39982                 return o;
39983             }
39984
39985             // get/set tokens. These are prefixed with the base URL so that `osm-auth`
39986             // can be used with multiple APIs and the keys in `localStorage`
39987             // will not clash
39988             var token;
39989
39990             if (store_legacy.enabled) {
39991                 token = function (x, y) {
39992                     if (arguments.length === 1) { return store_legacy.get(o.url + x); }
39993                     else if (arguments.length === 2) { return store_legacy.set(o.url + x, y); }
39994                 };
39995             } else {
39996                 var storage = {};
39997                 token = function (x, y) {
39998                     if (arguments.length === 1) { return storage[o.url + x]; }
39999                     else if (arguments.length === 2) { return storage[o.url + x] = y; }
40000                 };
40001             }
40002
40003             // Get an authentication object. If you just add and remove properties
40004             // from a single object, you'll need to use `delete` to make sure that
40005             // it doesn't contain undesired properties for authentication
40006             function getAuth(o) {
40007                 return {
40008                     oauth_consumer_key: o.oauth_consumer_key,
40009                     oauth_signature_method: 'HMAC-SHA1'
40010                 };
40011             }
40012
40013             // potentially pre-authorize
40014             oauth.options(o);
40015
40016             return oauth;
40017         };
40018
40019         var JXON = new (function () {
40020           var
40021             sValueProp = 'keyValue', sAttributesProp = 'keyAttributes', sAttrPref = '@', /* you can customize these values */
40022             aCache = [], rIsNull = /^\s*$/, rIsBool = /^(?:true|false)$/i;
40023
40024           function parseText (sValue) {
40025             if (rIsNull.test(sValue)) { return null; }
40026             if (rIsBool.test(sValue)) { return sValue.toLowerCase() === 'true'; }
40027             if (isFinite(sValue)) { return parseFloat(sValue); }
40028             if (isFinite(Date.parse(sValue))) { return new Date(sValue); }
40029             return sValue;
40030           }
40031
40032           function EmptyTree () { }
40033           EmptyTree.prototype.toString = function () { return 'null'; };
40034           EmptyTree.prototype.valueOf = function () { return null; };
40035
40036           function objectify (vValue) {
40037             return vValue === null ? new EmptyTree() : vValue instanceof Object ? vValue : new vValue.constructor(vValue);
40038           }
40039
40040           function createObjTree (oParentNode, nVerb, bFreeze, bNesteAttr) {
40041             var
40042               nLevelStart = aCache.length, bChildren = oParentNode.hasChildNodes(),
40043               bAttributes = oParentNode.hasAttributes(), bHighVerb = Boolean(nVerb & 2);
40044
40045             var
40046               sProp, vContent, nLength = 0, sCollectedTxt = '',
40047               vResult = bHighVerb ? {} : /* put here the default value for empty nodes: */ true;
40048
40049             if (bChildren) {
40050               for (var oNode, nItem = 0; nItem < oParentNode.childNodes.length; nItem++) {
40051                 oNode = oParentNode.childNodes.item(nItem);
40052                 if (oNode.nodeType === 4) { sCollectedTxt += oNode.nodeValue; } /* nodeType is 'CDATASection' (4) */
40053                 else if (oNode.nodeType === 3) { sCollectedTxt += oNode.nodeValue.trim(); } /* nodeType is 'Text' (3) */
40054                 else if (oNode.nodeType === 1 && !oNode.prefix) { aCache.push(oNode); } /* nodeType is 'Element' (1) */
40055               }
40056             }
40057
40058             var nLevelEnd = aCache.length, vBuiltVal = parseText(sCollectedTxt);
40059
40060             if (!bHighVerb && (bChildren || bAttributes)) { vResult = nVerb === 0 ? objectify(vBuiltVal) : {}; }
40061
40062             for (var nElId = nLevelStart; nElId < nLevelEnd; nElId++) {
40063               sProp = aCache[nElId].nodeName.toLowerCase();
40064               vContent = createObjTree(aCache[nElId], nVerb, bFreeze, bNesteAttr);
40065               if (vResult.hasOwnProperty(sProp)) {
40066                 if (vResult[sProp].constructor !== Array) { vResult[sProp] = [vResult[sProp]]; }
40067                 vResult[sProp].push(vContent);
40068               } else {
40069                 vResult[sProp] = vContent;
40070                 nLength++;
40071               }
40072             }
40073
40074             if (bAttributes) {
40075               var
40076                 nAttrLen = oParentNode.attributes.length,
40077                 sAPrefix = bNesteAttr ? '' : sAttrPref, oAttrParent = bNesteAttr ? {} : vResult;
40078
40079               for (var oAttrib, nAttrib = 0; nAttrib < nAttrLen; nLength++, nAttrib++) {
40080                 oAttrib = oParentNode.attributes.item(nAttrib);
40081                 oAttrParent[sAPrefix + oAttrib.name.toLowerCase()] = parseText(oAttrib.value.trim());
40082               }
40083
40084               if (bNesteAttr) {
40085                 if (bFreeze) { Object.freeze(oAttrParent); }
40086                 vResult[sAttributesProp] = oAttrParent;
40087                 nLength -= nAttrLen - 1;
40088               }
40089             }
40090
40091             if (nVerb === 3 || (nVerb === 2 || nVerb === 1 && nLength > 0) && sCollectedTxt) {
40092               vResult[sValueProp] = vBuiltVal;
40093             } else if (!bHighVerb && nLength === 0 && sCollectedTxt) {
40094               vResult = vBuiltVal;
40095             }
40096
40097             if (bFreeze && (bHighVerb || nLength > 0)) { Object.freeze(vResult); }
40098
40099             aCache.length = nLevelStart;
40100
40101             return vResult;
40102           }
40103
40104           function loadObjTree (oXMLDoc, oParentEl, oParentObj) {
40105             var vValue, oChild;
40106
40107             if (oParentObj instanceof String || oParentObj instanceof Number || oParentObj instanceof Boolean) {
40108               oParentEl.appendChild(oXMLDoc.createTextNode(oParentObj.toString())); /* verbosity level is 0 */
40109             } else if (oParentObj.constructor === Date) {
40110               oParentEl.appendChild(oXMLDoc.createTextNode(oParentObj.toGMTString()));    
40111             }
40112
40113             for (var sName in oParentObj) {
40114               vValue = oParentObj[sName];
40115               if (isFinite(sName) || vValue instanceof Function) { continue; } /* verbosity level is 0 */
40116               if (sName === sValueProp) {
40117                 if (vValue !== null && vValue !== true) { oParentEl.appendChild(oXMLDoc.createTextNode(vValue.constructor === Date ? vValue.toGMTString() : String(vValue))); }
40118               } else if (sName === sAttributesProp) { /* verbosity level is 3 */
40119                 for (var sAttrib in vValue) { oParentEl.setAttribute(sAttrib, vValue[sAttrib]); }
40120               } else if (sName.charAt(0) === sAttrPref) {
40121                 oParentEl.setAttribute(sName.slice(1), vValue);
40122               } else if (vValue.constructor === Array) {
40123                 for (var nItem = 0; nItem < vValue.length; nItem++) {
40124                   oChild = oXMLDoc.createElement(sName);
40125                   loadObjTree(oXMLDoc, oChild, vValue[nItem]);
40126                   oParentEl.appendChild(oChild);
40127                 }
40128               } else {
40129                 oChild = oXMLDoc.createElement(sName);
40130                 if (vValue instanceof Object) {
40131                   loadObjTree(oXMLDoc, oChild, vValue);
40132                 } else if (vValue !== null && vValue !== true) {
40133                   oChild.appendChild(oXMLDoc.createTextNode(vValue.toString()));
40134                 }
40135                 oParentEl.appendChild(oChild);
40136              }
40137            }
40138           }
40139
40140           this.build = function (oXMLParent, nVerbosity /* optional */, bFreeze /* optional */, bNesteAttributes /* optional */) {
40141             var _nVerb = arguments.length > 1 && typeof nVerbosity === 'number' ? nVerbosity & 3 : /* put here the default verbosity level: */ 1;
40142             return createObjTree(oXMLParent, _nVerb, bFreeze || false, arguments.length > 3 ? bNesteAttributes : _nVerb === 3);    
40143           };
40144
40145           this.unbuild = function (oObjTree) {    
40146             var oNewDoc = document.implementation.createDocument('', '', null);
40147             loadObjTree(oNewDoc, oNewDoc, oObjTree);
40148             return oNewDoc;
40149           };
40150
40151           this.stringify = function (oObjTree) {
40152             return (new XMLSerializer()).serializeToString(JXON.unbuild(oObjTree));
40153           };
40154         })();
40155
40156         // var myObject = JXON.build(doc);
40157         // we got our javascript object! try: alert(JSON.stringify(myObject));
40158
40159         // var newDoc = JXON.unbuild(myObject);
40160         // we got our Document instance! try: alert((new XMLSerializer()).serializeToString(newDoc));
40161
40162         var tiler$5 = utilTiler();
40163         var dispatch$6 = dispatch('apiStatusChange', 'authLoading', 'authDone', 'change', 'loading', 'loaded', 'loadedNotes');
40164         var urlroot = 'https://www.openstreetmap.org';
40165         var oauth = osmAuth({
40166             url: urlroot,
40167             oauth_consumer_key: '5A043yRSEugj4DJ5TljuapfnrflWDte8jTOcWLlT',
40168             oauth_secret: 'aB3jKq1TRsCOUrfOIZ6oQMEDmv2ptV76PA54NGLL',
40169             loading: authLoading,
40170             done: authDone
40171         });
40172         // hardcode default block of Google Maps
40173         var _imageryBlocklists = [/.*\.google(apis)?\..*\/(vt|kh)[\?\/].*([xyz]=.*){3}.*/];
40174         var _tileCache = { toLoad: {}, loaded: {}, inflight: {}, seen: {}, rtree: new RBush() };
40175         var _noteCache = { toLoad: {}, loaded: {}, inflight: {}, inflightPost: {}, note: {}, closed: {}, rtree: new RBush() };
40176         var _userCache = { toLoad: {}, user: {} };
40177         var _cachedApiStatus;
40178         var _changeset = {};
40179
40180         var _deferred = new Set();
40181         var _connectionID = 1;
40182         var _tileZoom$3 = 16;
40183         var _noteZoom = 12;
40184         var _rateLimitError;
40185         var _userChangesets;
40186         var _userDetails;
40187         var _off;
40188
40189         // set a default but also load this from the API status
40190         var _maxWayNodes = 2000;
40191
40192
40193         function authLoading() {
40194             dispatch$6.call('authLoading');
40195         }
40196
40197
40198         function authDone() {
40199             dispatch$6.call('authDone');
40200         }
40201
40202
40203         function abortRequest$5(controllerOrXHR) {
40204             if (controllerOrXHR) {
40205                 controllerOrXHR.abort();
40206             }
40207         }
40208
40209
40210         function hasInflightRequests(cache) {
40211             return Object.keys(cache.inflight).length;
40212         }
40213
40214
40215         function abortUnwantedRequests$3(cache, visibleTiles) {
40216             Object.keys(cache.inflight).forEach(function(k) {
40217                 if (cache.toLoad[k]) { return; }
40218                 if (visibleTiles.find(function(tile) { return k === tile.id; })) { return; }
40219
40220                 abortRequest$5(cache.inflight[k]);
40221                 delete cache.inflight[k];
40222             });
40223         }
40224
40225
40226         function getLoc(attrs) {
40227             var lon = attrs.lon && attrs.lon.value;
40228             var lat = attrs.lat && attrs.lat.value;
40229             return [parseFloat(lon), parseFloat(lat)];
40230         }
40231
40232
40233         function getNodes(obj) {
40234             var elems = obj.getElementsByTagName('nd');
40235             var nodes = new Array(elems.length);
40236             for (var i = 0, l = elems.length; i < l; i++) {
40237                 nodes[i] = 'n' + elems[i].attributes.ref.value;
40238             }
40239             return nodes;
40240         }
40241
40242         function getNodesJSON(obj) {
40243             var elems = obj.nodes;
40244             var nodes = new Array(elems.length);
40245             for (var i = 0, l = elems.length; i < l; i++) {
40246                 nodes[i] = 'n' + elems[i];
40247             }
40248             return nodes;
40249         }
40250
40251         function getTags(obj) {
40252             var elems = obj.getElementsByTagName('tag');
40253             var tags = {};
40254             for (var i = 0, l = elems.length; i < l; i++) {
40255                 var attrs = elems[i].attributes;
40256                 tags[attrs.k.value] = attrs.v.value;
40257             }
40258
40259             return tags;
40260         }
40261
40262
40263         function getMembers(obj) {
40264             var elems = obj.getElementsByTagName('member');
40265             var members = new Array(elems.length);
40266             for (var i = 0, l = elems.length; i < l; i++) {
40267                 var attrs = elems[i].attributes;
40268                 members[i] = {
40269                     id: attrs.type.value[0] + attrs.ref.value,
40270                     type: attrs.type.value,
40271                     role: attrs.role.value
40272                 };
40273             }
40274             return members;
40275         }
40276
40277         function getMembersJSON(obj) {
40278             var elems = obj.members;
40279             var members = new Array(elems.length);
40280             for (var i = 0, l = elems.length; i < l; i++) {
40281                 var attrs = elems[i];
40282                 members[i] = {
40283                     id: attrs.type[0] + attrs.ref,
40284                     type: attrs.type,
40285                     role: attrs.role
40286                 };
40287             }
40288             return members;
40289         }
40290
40291         function getVisible(attrs) {
40292             return (!attrs.visible || attrs.visible.value !== 'false');
40293         }
40294
40295
40296         function parseComments(comments) {
40297             var parsedComments = [];
40298
40299             // for each comment
40300             for (var i = 0; i < comments.length; i++) {
40301                 var comment = comments[i];
40302                 if (comment.nodeName === 'comment') {
40303                     var childNodes = comment.childNodes;
40304                     var parsedComment = {};
40305
40306                     for (var j = 0; j < childNodes.length; j++) {
40307                         var node = childNodes[j];
40308                         var nodeName = node.nodeName;
40309                         if (nodeName === '#text') { continue; }
40310                         parsedComment[nodeName] = node.textContent;
40311
40312                         if (nodeName === 'uid') {
40313                             var uid = node.textContent;
40314                             if (uid && !_userCache.user[uid]) {
40315                                 _userCache.toLoad[uid] = true;
40316                             }
40317                         }
40318                     }
40319
40320                     if (parsedComment) {
40321                         parsedComments.push(parsedComment);
40322                     }
40323                 }
40324             }
40325             return parsedComments;
40326         }
40327
40328
40329         function encodeNoteRtree(note) {
40330             return {
40331                 minX: note.loc[0],
40332                 minY: note.loc[1],
40333                 maxX: note.loc[0],
40334                 maxY: note.loc[1],
40335                 data: note
40336             };
40337         }
40338
40339
40340         var jsonparsers = {
40341
40342             node: function nodeData(obj, uid) {
40343                 return new osmNode({
40344                     id:  uid,
40345                     visible: typeof obj.visible === 'boolean' ? obj.visible : true,
40346                     version: obj.version && obj.version.toString(),
40347                     changeset: obj.changeset && obj.changeset.toString(),
40348                     timestamp: obj.timestamp,
40349                     user: obj.user,
40350                     uid: obj.uid && obj.uid.toString(),
40351                     loc: [parseFloat(obj.lon), parseFloat(obj.lat)],
40352                     tags: obj.tags
40353                 });
40354             },
40355
40356             way: function wayData(obj, uid) {
40357                 return new osmWay({
40358                     id:  uid,
40359                     visible: typeof obj.visible === 'boolean' ? obj.visible : true,
40360                     version: obj.version && obj.version.toString(),
40361                     changeset: obj.changeset && obj.changeset.toString(),
40362                     timestamp: obj.timestamp,
40363                     user: obj.user,
40364                     uid: obj.uid && obj.uid.toString(),
40365                     tags: obj.tags,
40366                     nodes: getNodesJSON(obj)
40367                 });
40368             },
40369
40370             relation: function relationData(obj, uid) {
40371                 return new osmRelation({
40372                     id:  uid,
40373                     visible: typeof obj.visible === 'boolean' ? obj.visible : true,
40374                     version: obj.version && obj.version.toString(),
40375                     changeset: obj.changeset && obj.changeset.toString(),
40376                     timestamp: obj.timestamp,
40377                     user: obj.user,
40378                     uid: obj.uid && obj.uid.toString(),
40379                     tags: obj.tags,
40380                     members: getMembersJSON(obj)
40381                 });
40382             }
40383         };
40384
40385         function parseJSON(payload, callback, options) {
40386             options = Object.assign({ skipSeen: true }, options);
40387             if (!payload)  {
40388                 return callback({ message: 'No JSON', status: -1 });
40389             }
40390
40391             var json = payload;
40392             if (typeof json !== 'object')
40393                { json = JSON.parse(payload); }
40394
40395             if (!json.elements)
40396                 { return callback({ message: 'No JSON', status: -1 }); }
40397
40398             var children = json.elements;
40399
40400             var handle = window.requestIdleCallback(function() {
40401                 var results = [];
40402                 var result;
40403                 for (var i = 0; i < children.length; i++) {
40404                     result = parseChild(children[i]);
40405                     if (result) { results.push(result); }
40406                 }
40407                 callback(null, results);
40408             });
40409
40410             _deferred.add(handle);
40411
40412             function parseChild(child) {
40413                 var parser = jsonparsers[child.type];
40414                 if (!parser) { return null; }
40415
40416                 var uid;
40417
40418                 uid = osmEntity.id.fromOSM(child.type, child.id);
40419                 if (options.skipSeen) {
40420                     if (_tileCache.seen[uid]) { return null; }  // avoid reparsing a "seen" entity
40421                     _tileCache.seen[uid] = true;
40422                 }
40423
40424                 return parser(child, uid);
40425             }
40426         }
40427
40428         var parsers = {
40429             node: function nodeData(obj, uid) {
40430                 var attrs = obj.attributes;
40431                 return new osmNode({
40432                     id: uid,
40433                     visible: getVisible(attrs),
40434                     version: attrs.version.value,
40435                     changeset: attrs.changeset && attrs.changeset.value,
40436                     timestamp: attrs.timestamp && attrs.timestamp.value,
40437                     user: attrs.user && attrs.user.value,
40438                     uid: attrs.uid && attrs.uid.value,
40439                     loc: getLoc(attrs),
40440                     tags: getTags(obj)
40441                 });
40442             },
40443
40444             way: function wayData(obj, uid) {
40445                 var attrs = obj.attributes;
40446                 return new osmWay({
40447                     id: uid,
40448                     visible: getVisible(attrs),
40449                     version: attrs.version.value,
40450                     changeset: attrs.changeset && attrs.changeset.value,
40451                     timestamp: attrs.timestamp && attrs.timestamp.value,
40452                     user: attrs.user && attrs.user.value,
40453                     uid: attrs.uid && attrs.uid.value,
40454                     tags: getTags(obj),
40455                     nodes: getNodes(obj),
40456                 });
40457             },
40458
40459             relation: function relationData(obj, uid) {
40460                 var attrs = obj.attributes;
40461                 return new osmRelation({
40462                     id: uid,
40463                     visible: getVisible(attrs),
40464                     version: attrs.version.value,
40465                     changeset: attrs.changeset && attrs.changeset.value,
40466                     timestamp: attrs.timestamp && attrs.timestamp.value,
40467                     user: attrs.user && attrs.user.value,
40468                     uid: attrs.uid && attrs.uid.value,
40469                     tags: getTags(obj),
40470                     members: getMembers(obj)
40471                 });
40472             },
40473
40474             note: function parseNote(obj, uid) {
40475                 var attrs = obj.attributes;
40476                 var childNodes = obj.childNodes;
40477                 var props = {};
40478
40479                 props.id = uid;
40480                 props.loc = getLoc(attrs);
40481
40482                 // if notes are coincident, move them apart slightly
40483                 var coincident = false;
40484                 var epsilon = 0.00001;
40485                 do {
40486                     if (coincident) {
40487                         props.loc = geoVecAdd(props.loc, [epsilon, epsilon]);
40488                     }
40489                     var bbox = geoExtent(props.loc).bbox();
40490                     coincident = _noteCache.rtree.search(bbox).length;
40491                 } while (coincident);
40492
40493                 // parse note contents
40494                 for (var i = 0; i < childNodes.length; i++) {
40495                     var node = childNodes[i];
40496                     var nodeName = node.nodeName;
40497                     if (nodeName === '#text') { continue; }
40498
40499                     // if the element is comments, parse the comments
40500                     if (nodeName === 'comments') {
40501                         props[nodeName] = parseComments(node.childNodes);
40502                     } else {
40503                         props[nodeName] = node.textContent;
40504                     }
40505                 }
40506
40507                 var note = new osmNote(props);
40508                 var item = encodeNoteRtree(note);
40509                 _noteCache.note[note.id] = note;
40510                 _noteCache.rtree.insert(item);
40511
40512                 return note;
40513             },
40514
40515             user: function parseUser(obj, uid) {
40516                 var attrs = obj.attributes;
40517                 var user = {
40518                     id: uid,
40519                     display_name: attrs.display_name && attrs.display_name.value,
40520                     account_created: attrs.account_created && attrs.account_created.value,
40521                     changesets_count: '0',
40522                     active_blocks: '0'
40523                 };
40524
40525                 var img = obj.getElementsByTagName('img');
40526                 if (img && img[0] && img[0].getAttribute('href')) {
40527                     user.image_url = img[0].getAttribute('href');
40528                 }
40529
40530                 var changesets = obj.getElementsByTagName('changesets');
40531                 if (changesets && changesets[0] && changesets[0].getAttribute('count')) {
40532                     user.changesets_count = changesets[0].getAttribute('count');
40533                 }
40534
40535                 var blocks = obj.getElementsByTagName('blocks');
40536                 if (blocks && blocks[0]) {
40537                     var received = blocks[0].getElementsByTagName('received');
40538                     if (received && received[0] && received[0].getAttribute('active')) {
40539                         user.active_blocks = received[0].getAttribute('active');
40540                     }
40541                 }
40542
40543                 _userCache.user[uid] = user;
40544                 delete _userCache.toLoad[uid];
40545                 return user;
40546             }
40547         };
40548
40549
40550         function parseXML(xml, callback, options) {
40551             options = Object.assign({ skipSeen: true }, options);
40552             if (!xml || !xml.childNodes) {
40553                 return callback({ message: 'No XML', status: -1 });
40554             }
40555
40556             var root = xml.childNodes[0];
40557             var children = root.childNodes;
40558
40559             var handle = window.requestIdleCallback(function() {
40560                 var results = [];
40561                 var result;
40562                 for (var i = 0; i < children.length; i++) {
40563                     result = parseChild(children[i]);
40564                     if (result) { results.push(result); }
40565                 }
40566                 callback(null, results);
40567             });
40568
40569             _deferred.add(handle);
40570
40571
40572             function parseChild(child) {
40573                 var parser = parsers[child.nodeName];
40574                 if (!parser) { return null; }
40575
40576                 var uid;
40577                 if (child.nodeName === 'user') {
40578                     uid = child.attributes.id.value;
40579                     if (options.skipSeen && _userCache.user[uid]) {
40580                         delete _userCache.toLoad[uid];
40581                         return null;
40582                     }
40583
40584                 } else if (child.nodeName === 'note') {
40585                     uid = child.getElementsByTagName('id')[0].textContent;
40586
40587                 } else {
40588                     uid = osmEntity.id.fromOSM(child.nodeName, child.attributes.id.value);
40589                     if (options.skipSeen) {
40590                         if (_tileCache.seen[uid]) { return null; }  // avoid reparsing a "seen" entity
40591                         _tileCache.seen[uid] = true;
40592                     }
40593                 }
40594
40595                 return parser(child, uid);
40596             }
40597         }
40598
40599
40600         // replace or remove note from rtree
40601         function updateRtree$3(item, replace) {
40602             _noteCache.rtree.remove(item, function isEql(a, b) { return a.data.id === b.data.id; });
40603
40604             if (replace) {
40605                 _noteCache.rtree.insert(item);
40606             }
40607         }
40608
40609
40610         function wrapcb(thisArg, callback, cid) {
40611             return function(err, result) {
40612                 if (err) {
40613                     // 400 Bad Request, 401 Unauthorized, 403 Forbidden..
40614                     if (err.status === 400 || err.status === 401 || err.status === 403) {
40615                         thisArg.logout();
40616                     }
40617                     return callback.call(thisArg, err);
40618
40619                 } else if (thisArg.getConnectionId() !== cid) {
40620                     return callback.call(thisArg, { message: 'Connection Switched', status: -1 });
40621
40622                 } else {
40623                     return callback.call(thisArg, err, result);
40624                 }
40625             };
40626         }
40627
40628
40629         var serviceOsm = {
40630
40631             init: function() {
40632                 utilRebind(this, dispatch$6, 'on');
40633             },
40634
40635
40636             reset: function() {
40637                 Array.from(_deferred).forEach(function(handle) {
40638                     window.cancelIdleCallback(handle);
40639                     _deferred.delete(handle);
40640                 });
40641
40642                 _connectionID++;
40643                 _userChangesets = undefined;
40644                 _userDetails = undefined;
40645                 _rateLimitError = undefined;
40646
40647                 Object.values(_tileCache.inflight).forEach(abortRequest$5);
40648                 Object.values(_noteCache.inflight).forEach(abortRequest$5);
40649                 Object.values(_noteCache.inflightPost).forEach(abortRequest$5);
40650                 if (_changeset.inflight) { abortRequest$5(_changeset.inflight); }
40651
40652                 _tileCache = { toLoad: {}, loaded: {}, inflight: {}, seen: {}, rtree: new RBush() };
40653                 _noteCache = { toLoad: {}, loaded: {}, inflight: {}, inflightPost: {}, note: {}, closed: {}, rtree: new RBush() };
40654                 _userCache = { toLoad: {}, user: {} };
40655                 _cachedApiStatus = undefined;
40656                 _changeset = {};
40657
40658                 return this;
40659             },
40660
40661
40662             getConnectionId: function() {
40663                 return _connectionID;
40664             },
40665
40666
40667             changesetURL: function(changesetID) {
40668                 return urlroot + '/changeset/' + changesetID;
40669             },
40670
40671
40672             changesetsURL: function(center, zoom) {
40673                 var precision = Math.max(0, Math.ceil(Math.log(zoom) / Math.LN2));
40674                 return urlroot + '/history#map=' +
40675                     Math.floor(zoom) + '/' +
40676                     center[1].toFixed(precision) + '/' +
40677                     center[0].toFixed(precision);
40678             },
40679
40680
40681             entityURL: function(entity) {
40682                 return urlroot + '/' + entity.type + '/' + entity.osmId();
40683             },
40684
40685
40686             historyURL: function(entity) {
40687                 return urlroot + '/' + entity.type + '/' + entity.osmId() + '/history';
40688             },
40689
40690
40691             userURL: function(username) {
40692                 return urlroot + '/user/' + username;
40693             },
40694
40695
40696             noteURL: function(note) {
40697                 return urlroot + '/note/' + note.id;
40698             },
40699
40700
40701             noteReportURL: function(note) {
40702                 return urlroot + '/reports/new?reportable_type=Note&reportable_id=' + note.id;
40703             },
40704
40705
40706             // Generic method to load data from the OSM API
40707             // Can handle either auth or unauth calls.
40708             loadFromAPI: function(path, callback, options) {
40709                 options = Object.assign({ skipSeen: true }, options);
40710                 var that = this;
40711                 var cid = _connectionID;
40712
40713                 function done(err, payload) {
40714                     if (that.getConnectionId() !== cid) {
40715                         if (callback) { callback({ message: 'Connection Switched', status: -1 }); }
40716                         return;
40717                     }
40718
40719                     var isAuthenticated = that.authenticated();
40720
40721                     // 400 Bad Request, 401 Unauthorized, 403 Forbidden
40722                     // Logout and retry the request..
40723                     if (isAuthenticated && err && err.status &&
40724                             (err.status === 400 || err.status === 401 || err.status === 403)) {
40725                         that.logout();
40726                         that.loadFromAPI(path, callback, options);
40727
40728                     // else, no retry..
40729                     } else {
40730                         // 509 Bandwidth Limit Exceeded, 429 Too Many Requests
40731                         // Set the rateLimitError flag and trigger a warning..
40732                         if (!isAuthenticated && !_rateLimitError && err && err.status &&
40733                                 (err.status === 509 || err.status === 429)) {
40734                             _rateLimitError = err;
40735                             dispatch$6.call('change');
40736                             that.reloadApiStatus();
40737
40738                         } else if ((err && _cachedApiStatus === 'online') ||
40739                             (!err && _cachedApiStatus !== 'online')) {
40740                             // If the response's error state doesn't match the status,
40741                             // it's likely we lost or gained the connection so reload the status
40742                             that.reloadApiStatus();
40743                         }
40744
40745                         if (callback) {
40746                             if (err) {
40747                                 return callback(err);
40748                             } else {
40749                                 if (path.indexOf('.json') !== -1) {
40750                                     return parseJSON(payload, callback, options);
40751                                 } else {
40752                                     return parseXML(payload, callback, options);
40753                                 }
40754                             }
40755                         }
40756                     }
40757                 }
40758
40759                 if (this.authenticated()) {
40760                     return oauth.xhr({ method: 'GET', path: path }, done);
40761                 } else {
40762                     var url = urlroot + path;
40763                     var controller = new AbortController();
40764                     d3_json(url, { signal: controller.signal })
40765                         .then(function(data) {
40766                             done(null, data);
40767                         })
40768                         .catch(function(err) {
40769                             if (err.name === 'AbortError') { return; }
40770                             // d3-fetch includes status in the error message,
40771                             // but we can't access the response itself
40772                             // https://github.com/d3/d3-fetch/issues/27
40773                             var match = err.message.match(/^\d{3}/);
40774                             if (match) {
40775                                 done({ status: +match[0], statusText: err.message });
40776                             } else {
40777                                 done(err.message);
40778                             }
40779                         });
40780                     return controller;
40781                 }
40782             },
40783
40784
40785             // Load a single entity by id (ways and relations use the `/full` call)
40786             // GET /api/0.6/node/#id
40787             // GET /api/0.6/[way|relation]/#id/full
40788             loadEntity: function(id, callback) {
40789                 var type = osmEntity.id.type(id);
40790                 var osmID = osmEntity.id.toOSM(id);
40791                 var options = { skipSeen: false };
40792
40793                 this.loadFromAPI(
40794                     '/api/0.6/' + type + '/' + osmID + (type !== 'node' ? '/full' : '') + '.json',
40795                     function(err, entities) {
40796                         if (callback) { callback(err, { data: entities }); }
40797                     },
40798                     options
40799                 );
40800             },
40801
40802
40803             // Load a single entity with a specific version
40804             // GET /api/0.6/[node|way|relation]/#id/#version
40805             loadEntityVersion: function(id, version, callback) {
40806                 var type = osmEntity.id.type(id);
40807                 var osmID = osmEntity.id.toOSM(id);
40808                 var options = { skipSeen: false };
40809
40810                 this.loadFromAPI(
40811                     '/api/0.6/' + type + '/' + osmID + '/' + version + '.json',
40812                     function(err, entities) {
40813                         if (callback) { callback(err, { data: entities }); }
40814                     },
40815                     options
40816                 );
40817             },
40818
40819
40820             // Load multiple entities in chunks
40821             // (note: callback may be called multiple times)
40822             // Unlike `loadEntity`, child nodes and members are not fetched
40823             // GET /api/0.6/[nodes|ways|relations]?#parameters
40824             loadMultiple: function(ids, callback) {
40825                 var that = this;
40826                 var groups = utilArrayGroupBy(utilArrayUniq(ids), osmEntity.id.type);
40827
40828                 Object.keys(groups).forEach(function(k) {
40829                     var type = k + 's';   // nodes, ways, relations
40830                     var osmIDs = groups[k].map(function(id) { return osmEntity.id.toOSM(id); });
40831                     var options = { skipSeen: false };
40832
40833                     utilArrayChunk(osmIDs, 150).forEach(function(arr) {
40834                         that.loadFromAPI(
40835                             '/api/0.6/' + type + '.json?' + type + '=' + arr.join(),
40836                             function(err, entities) {
40837                                 if (callback) { callback(err, { data: entities }); }
40838                             },
40839                             options
40840                         );
40841                     });
40842                 });
40843             },
40844
40845
40846             // Create, upload, and close a changeset
40847             // PUT /api/0.6/changeset/create
40848             // POST /api/0.6/changeset/#id/upload
40849             // PUT /api/0.6/changeset/#id/close
40850             putChangeset: function(changeset, changes, callback) {
40851                 var cid = _connectionID;
40852
40853                 if (_changeset.inflight) {
40854                     return callback({ message: 'Changeset already inflight', status: -2 }, changeset);
40855
40856                 } else if (_changeset.open) {   // reuse existing open changeset..
40857                     return createdChangeset.call(this, null, _changeset.open);
40858
40859                 } else {   // Open a new changeset..
40860                     var options = {
40861                         method: 'PUT',
40862                         path: '/api/0.6/changeset/create',
40863                         options: { header: { 'Content-Type': 'text/xml' } },
40864                         content: JXON.stringify(changeset.asJXON())
40865                     };
40866                     _changeset.inflight = oauth.xhr(
40867                         options,
40868                         wrapcb(this, createdChangeset, cid)
40869                     );
40870                 }
40871
40872
40873                 function createdChangeset(err, changesetID) {
40874                     _changeset.inflight = null;
40875                     if (err) { return callback(err, changeset); }
40876
40877                     _changeset.open = changesetID;
40878                     changeset = changeset.update({ id: changesetID });
40879
40880                     // Upload the changeset..
40881                     var options = {
40882                         method: 'POST',
40883                         path: '/api/0.6/changeset/' + changesetID + '/upload',
40884                         options: { header: { 'Content-Type': 'text/xml' } },
40885                         content: JXON.stringify(changeset.osmChangeJXON(changes))
40886                     };
40887                     _changeset.inflight = oauth.xhr(
40888                         options,
40889                         wrapcb(this, uploadedChangeset, cid)
40890                     );
40891                 }
40892
40893
40894                 function uploadedChangeset(err) {
40895                     _changeset.inflight = null;
40896                     if (err) { return callback(err, changeset); }
40897
40898                     // Upload was successful, safe to call the callback.
40899                     // Add delay to allow for postgres replication #1646 #2678
40900                     window.setTimeout(function() { callback(null, changeset); }, 2500);
40901                     _changeset.open = null;
40902
40903                     // At this point, we don't really care if the connection was switched..
40904                     // Only try to close the changeset if we're still talking to the same server.
40905                     if (this.getConnectionId() === cid) {
40906                         // Still attempt to close changeset, but ignore response because #2667
40907                         oauth.xhr({
40908                             method: 'PUT',
40909                             path: '/api/0.6/changeset/' + changeset.id + '/close',
40910                             options: { header: { 'Content-Type': 'text/xml' } }
40911                         }, function() { return true; });
40912                     }
40913                 }
40914             },
40915
40916
40917             // Load multiple users in chunks
40918             // (note: callback may be called multiple times)
40919             // GET /api/0.6/users?users=#id1,#id2,...,#idn
40920             loadUsers: function(uids, callback) {
40921                 var toLoad = [];
40922                 var cached = [];
40923
40924                 utilArrayUniq(uids).forEach(function(uid) {
40925                     if (_userCache.user[uid]) {
40926                         delete _userCache.toLoad[uid];
40927                         cached.push(_userCache.user[uid]);
40928                     } else {
40929                         toLoad.push(uid);
40930                     }
40931                 });
40932
40933                 if (cached.length || !this.authenticated()) {
40934                     callback(undefined, cached);
40935                     if (!this.authenticated()) { return; }  // require auth
40936                 }
40937
40938                 utilArrayChunk(toLoad, 150).forEach(function(arr) {
40939                     oauth.xhr(
40940                         { method: 'GET', path: '/api/0.6/users?users=' + arr.join() },
40941                         wrapcb(this, done, _connectionID)
40942                     );
40943                 }.bind(this));
40944
40945                 function done(err, xml) {
40946                     if (err) { return callback(err); }
40947
40948                     var options = { skipSeen: true };
40949                     return parseXML(xml, function(err, results) {
40950                         if (err) {
40951                             return callback(err);
40952                         } else {
40953                             return callback(undefined, results);
40954                         }
40955                     }, options);
40956                 }
40957             },
40958
40959
40960             // Load a given user by id
40961             // GET /api/0.6/user/#id
40962             loadUser: function(uid, callback) {
40963                 if (_userCache.user[uid] || !this.authenticated()) {   // require auth
40964                     delete _userCache.toLoad[uid];
40965                     return callback(undefined, _userCache.user[uid]);
40966                 }
40967
40968                 oauth.xhr(
40969                     { method: 'GET', path: '/api/0.6/user/' + uid },
40970                     wrapcb(this, done, _connectionID)
40971                 );
40972
40973                 function done(err, xml) {
40974                     if (err) { return callback(err); }
40975
40976                     var options = { skipSeen: true };
40977                     return parseXML(xml, function(err, results) {
40978                         if (err) {
40979                             return callback(err);
40980                         } else {
40981                             return callback(undefined, results[0]);
40982                         }
40983                     }, options);
40984                 }
40985             },
40986
40987
40988             // Load the details of the logged-in user
40989             // GET /api/0.6/user/details
40990             userDetails: function(callback) {
40991                 if (_userDetails) {    // retrieve cached
40992                     return callback(undefined, _userDetails);
40993                 }
40994
40995                 oauth.xhr(
40996                     { method: 'GET', path: '/api/0.6/user/details' },
40997                     wrapcb(this, done, _connectionID)
40998                 );
40999
41000                 function done(err, xml) {
41001                     if (err) { return callback(err); }
41002
41003                     var options = { skipSeen: false };
41004                     return parseXML(xml, function(err, results) {
41005                         if (err) {
41006                             return callback(err);
41007                         } else {
41008                             _userDetails = results[0];
41009                             return callback(undefined, _userDetails);
41010                         }
41011                     }, options);
41012                 }
41013             },
41014
41015
41016             // Load previous changesets for the logged in user
41017             // GET /api/0.6/changesets?user=#id
41018             userChangesets: function(callback) {
41019                 if (_userChangesets) {    // retrieve cached
41020                     return callback(undefined, _userChangesets);
41021                 }
41022
41023                 this.userDetails(
41024                     wrapcb(this, gotDetails, _connectionID)
41025                 );
41026
41027
41028                 function gotDetails(err, user) {
41029                     if (err) { return callback(err); }
41030
41031                     oauth.xhr(
41032                         { method: 'GET', path: '/api/0.6/changesets?user=' + user.id },
41033                         wrapcb(this, done, _connectionID)
41034                     );
41035                 }
41036
41037                 function done(err, xml) {
41038                     if (err) { return callback(err); }
41039
41040                     _userChangesets = Array.prototype.map.call(
41041                         xml.getElementsByTagName('changeset'),
41042                         function (changeset) { return { tags: getTags(changeset) }; }
41043                     ).filter(function (changeset) {
41044                         var comment = changeset.tags.comment;
41045                         return comment && comment !== '';
41046                     });
41047
41048                     return callback(undefined, _userChangesets);
41049                 }
41050             },
41051
41052
41053             // Fetch the status of the OSM API
41054             // GET /api/capabilities
41055             status: function(callback) {
41056                 var url = urlroot + '/api/capabilities';
41057                 var errback = wrapcb(this, done, _connectionID);
41058                 d3_xml(url)
41059                     .then(function(data) { errback(null, data); })
41060                     .catch(function(err) { errback(err.message); });
41061
41062                 function done(err, xml) {
41063                     if (err) {
41064                         // the status is null if no response could be retrieved
41065                         return callback(err, null);
41066                     }
41067
41068                     // update blocklists
41069                     var elements = xml.getElementsByTagName('blacklist');
41070                     var regexes = [];
41071                     for (var i = 0; i < elements.length; i++) {
41072                         var regexString = elements[i].getAttribute('regex');  // needs unencode?
41073                         if (regexString) {
41074                             try {
41075                                 var regex = new RegExp(regexString);
41076                                 regexes.push(regex);
41077                             } catch (e) {
41078                                 /* noop */
41079                             }
41080                         }
41081                     }
41082                     if (regexes.length) {
41083                         _imageryBlocklists = regexes;
41084                     }
41085
41086                     if (_rateLimitError) {
41087                         return callback(_rateLimitError, 'rateLimited');
41088                     } else {
41089                         var waynodes = xml.getElementsByTagName('waynodes');
41090                         var maxWayNodes = waynodes.length && parseInt(waynodes[0].getAttribute('maximum'), 10);
41091                         if (maxWayNodes && isFinite(maxWayNodes)) { _maxWayNodes = maxWayNodes; }
41092
41093                         var apiStatus = xml.getElementsByTagName('status');
41094                         var val = apiStatus[0].getAttribute('api');
41095                         return callback(undefined, val);
41096                     }
41097                 }
41098             },
41099
41100             // Calls `status` and dispatches an `apiStatusChange` event if the returned
41101             // status differs from the cached status.
41102             reloadApiStatus: function() {
41103                 // throttle to avoid unnecessary API calls
41104                 if (!this.throttledReloadApiStatus) {
41105                     var that = this;
41106                     this.throttledReloadApiStatus = throttle(function() {
41107                         that.status(function(err, status) {
41108                             if (status !== _cachedApiStatus) {
41109                                 _cachedApiStatus = status;
41110                                 dispatch$6.call('apiStatusChange', that, err, status);
41111                             }
41112                         });
41113                     }, 500);
41114                 }
41115                 this.throttledReloadApiStatus();
41116             },
41117
41118
41119             // Returns the maximum number of nodes a single way can have
41120             maxWayNodes: function() {
41121                 return _maxWayNodes;
41122             },
41123
41124
41125             // Load data (entities) from the API in tiles
41126             // GET /api/0.6/map?bbox=
41127             loadTiles: function(projection, callback) {
41128                 if (_off) { return; }
41129
41130                 // determine the needed tiles to cover the view
41131                 var tiles = tiler$5.zoomExtent([_tileZoom$3, _tileZoom$3]).getTiles(projection);
41132
41133                 // abort inflight requests that are no longer needed
41134                 var hadRequests = hasInflightRequests(_tileCache);
41135                 abortUnwantedRequests$3(_tileCache, tiles);
41136                 if (hadRequests && !hasInflightRequests(_tileCache)) {
41137                     dispatch$6.call('loaded');    // stop the spinner
41138                 }
41139
41140                 // issue new requests..
41141                 tiles.forEach(function(tile) {
41142                     this.loadTile(tile, callback);
41143                 }, this);
41144             },
41145
41146
41147             // Load a single data tile
41148             // GET /api/0.6/map?bbox=
41149             loadTile: function(tile, callback) {
41150                 if (_off) { return; }
41151                 if (_tileCache.loaded[tile.id] || _tileCache.inflight[tile.id]) { return; }
41152
41153                 if (!hasInflightRequests(_tileCache)) {
41154                     dispatch$6.call('loading');   // start the spinner
41155                 }
41156
41157                 var path = '/api/0.6/map.json?bbox=';
41158                 var options = { skipSeen: true };
41159
41160                 _tileCache.inflight[tile.id] = this.loadFromAPI(
41161                     path + tile.extent.toParam(),
41162                     tileCallback,
41163                     options
41164                 );
41165
41166                 function tileCallback(err, parsed) {
41167                     delete _tileCache.inflight[tile.id];
41168                     if (!err) {
41169                         delete _tileCache.toLoad[tile.id];
41170                         _tileCache.loaded[tile.id] = true;
41171                         var bbox = tile.extent.bbox();
41172                         bbox.id = tile.id;
41173                         _tileCache.rtree.insert(bbox);
41174                     }
41175                     if (callback) {
41176                         callback(err, Object.assign({ data: parsed }, tile));
41177                     }
41178                     if (!hasInflightRequests(_tileCache)) {
41179                         dispatch$6.call('loaded');     // stop the spinner
41180                     }
41181                 }
41182             },
41183
41184
41185             isDataLoaded: function(loc) {
41186                 var bbox = { minX: loc[0], minY: loc[1], maxX: loc[0], maxY: loc[1] };
41187                 return _tileCache.rtree.collides(bbox);
41188             },
41189
41190
41191             // load the tile that covers the given `loc`
41192             loadTileAtLoc: function(loc, callback) {
41193                 // Back off if the toLoad queue is filling up.. re #6417
41194                 // (Currently `loadTileAtLoc` requests are considered low priority - used by operations to
41195                 // let users safely edit geometries which extend to unloaded tiles.  We can drop some.)
41196                 if (Object.keys(_tileCache.toLoad).length > 50) { return; }
41197
41198                 var k = geoZoomToScale(_tileZoom$3 + 1);
41199                 var offset = geoRawMercator().scale(k)(loc);
41200                 var projection = geoRawMercator().transform({ k: k, x: -offset[0], y: -offset[1] });
41201                 var tiles = tiler$5.zoomExtent([_tileZoom$3, _tileZoom$3]).getTiles(projection);
41202
41203                 tiles.forEach(function(tile) {
41204                     if (_tileCache.toLoad[tile.id] || _tileCache.loaded[tile.id] || _tileCache.inflight[tile.id]) { return; }
41205
41206                     _tileCache.toLoad[tile.id] = true;
41207                     this.loadTile(tile, callback);
41208                 }, this);
41209             },
41210
41211
41212             // Load notes from the API in tiles
41213             // GET /api/0.6/notes?bbox=
41214             loadNotes: function(projection, noteOptions) {
41215                 noteOptions = Object.assign({ limit: 10000, closed: 7 }, noteOptions);
41216                 if (_off) { return; }
41217
41218                 var that = this;
41219                 var path = '/api/0.6/notes?limit=' + noteOptions.limit + '&closed=' + noteOptions.closed + '&bbox=';
41220                 var throttleLoadUsers = throttle(function() {
41221                     var uids = Object.keys(_userCache.toLoad);
41222                     if (!uids.length) { return; }
41223                     that.loadUsers(uids, function() {});  // eagerly load user details
41224                 }, 750);
41225
41226                 // determine the needed tiles to cover the view
41227                 var tiles = tiler$5.zoomExtent([_noteZoom, _noteZoom]).getTiles(projection);
41228
41229                 // abort inflight requests that are no longer needed
41230                 abortUnwantedRequests$3(_noteCache, tiles);
41231
41232                 // issue new requests..
41233                 tiles.forEach(function(tile) {
41234                     if (_noteCache.loaded[tile.id] || _noteCache.inflight[tile.id]) { return; }
41235
41236                     var options = { skipSeen: false };
41237                     _noteCache.inflight[tile.id] = that.loadFromAPI(
41238                         path + tile.extent.toParam(),
41239                         function(err) {
41240                             delete _noteCache.inflight[tile.id];
41241                             if (!err) {
41242                                 _noteCache.loaded[tile.id] = true;
41243                             }
41244                             throttleLoadUsers();
41245                             dispatch$6.call('loadedNotes');
41246                         },
41247                         options
41248                     );
41249                 });
41250             },
41251
41252
41253             // Create a note
41254             // POST /api/0.6/notes?params
41255             postNoteCreate: function(note, callback) {
41256                 if (!this.authenticated()) {
41257                     return callback({ message: 'Not Authenticated', status: -3 }, note);
41258                 }
41259                 if (_noteCache.inflightPost[note.id]) {
41260                     return callback({ message: 'Note update already inflight', status: -2 }, note);
41261                 }
41262
41263                 if (!note.loc[0] || !note.loc[1] || !note.newComment) { return; } // location & description required
41264
41265                 var comment = note.newComment;
41266                 if (note.newCategory && note.newCategory !== 'None') { comment += ' #' + note.newCategory; }
41267
41268                 var path = '/api/0.6/notes?' + utilQsString({ lon: note.loc[0], lat: note.loc[1], text: comment });
41269
41270                 _noteCache.inflightPost[note.id] = oauth.xhr(
41271                     { method: 'POST', path: path },
41272                     wrapcb(this, done, _connectionID)
41273                 );
41274
41275
41276                 function done(err, xml) {
41277                     delete _noteCache.inflightPost[note.id];
41278                     if (err) { return callback(err); }
41279
41280                     // we get the updated note back, remove from caches and reparse..
41281                     this.removeNote(note);
41282
41283                     var options = { skipSeen: false };
41284                     return parseXML(xml, function(err, results) {
41285                         if (err) {
41286                             return callback(err);
41287                         } else {
41288                             return callback(undefined, results[0]);
41289                         }
41290                     }, options);
41291                 }
41292             },
41293
41294
41295             // Update a note
41296             // POST /api/0.6/notes/#id/comment?text=comment
41297             // POST /api/0.6/notes/#id/close?text=comment
41298             // POST /api/0.6/notes/#id/reopen?text=comment
41299             postNoteUpdate: function(note, newStatus, callback) {
41300                 if (!this.authenticated()) {
41301                     return callback({ message: 'Not Authenticated', status: -3 }, note);
41302                 }
41303                 if (_noteCache.inflightPost[note.id]) {
41304                     return callback({ message: 'Note update already inflight', status: -2 }, note);
41305                 }
41306
41307                 var action;
41308                 if (note.status !== 'closed' && newStatus === 'closed') {
41309                     action = 'close';
41310                 } else if (note.status !== 'open' && newStatus === 'open') {
41311                     action = 'reopen';
41312                 } else {
41313                     action = 'comment';
41314                     if (!note.newComment) { return; } // when commenting, comment required
41315                 }
41316
41317                 var path = '/api/0.6/notes/' + note.id + '/' + action;
41318                 if (note.newComment) {
41319                     path += '?' + utilQsString({ text: note.newComment });
41320                 }
41321
41322                 _noteCache.inflightPost[note.id] = oauth.xhr(
41323                     { method: 'POST', path: path },
41324                     wrapcb(this, done, _connectionID)
41325                 );
41326
41327
41328                 function done(err, xml) {
41329                     delete _noteCache.inflightPost[note.id];
41330                     if (err) { return callback(err); }
41331
41332                     // we get the updated note back, remove from caches and reparse..
41333                     this.removeNote(note);
41334
41335                     // update closed note cache - used to populate `closed:note` changeset tag
41336                     if (action === 'close') {
41337                         _noteCache.closed[note.id] = true;
41338                     } else if (action === 'reopen') {
41339                         delete _noteCache.closed[note.id];
41340                     }
41341
41342                     var options = { skipSeen: false };
41343                     return parseXML(xml, function(err, results) {
41344                         if (err) {
41345                             return callback(err);
41346                         } else {
41347                             return callback(undefined, results[0]);
41348                         }
41349                     }, options);
41350                 }
41351             },
41352
41353
41354             switch: function(options) {
41355                 urlroot = options.urlroot;
41356
41357                 oauth.options(Object.assign({
41358                     url: urlroot,
41359                     loading: authLoading,
41360                     done: authDone
41361                 }, options));
41362
41363                 this.reset();
41364                 this.userChangesets(function() {});  // eagerly load user details/changesets
41365                 dispatch$6.call('change');
41366                 return this;
41367             },
41368
41369
41370             toggle: function(val) {
41371                 _off = !val;
41372                 return this;
41373             },
41374
41375
41376             isChangesetInflight: function() {
41377                 return !!_changeset.inflight;
41378             },
41379
41380
41381             // get/set cached data
41382             // This is used to save/restore the state when entering/exiting the walkthrough
41383             // Also used for testing purposes.
41384             caches: function(obj) {
41385                 function cloneCache(source) {
41386                     var target = {};
41387                     Object.keys(source).forEach(function(k) {
41388                         if (k === 'rtree') {
41389                             target.rtree = new RBush().fromJSON(source.rtree.toJSON());  // clone rbush
41390                         } else if (k === 'note') {
41391                             target.note = {};
41392                             Object.keys(source.note).forEach(function(id) {
41393                                 target.note[id] = osmNote(source.note[id]);   // copy notes
41394                             });
41395                         } else {
41396                             target[k] = JSON.parse(JSON.stringify(source[k]));   // clone deep
41397                         }
41398                     });
41399                     return target;
41400                 }
41401
41402                 if (!arguments.length) {
41403                     return {
41404                         tile: cloneCache(_tileCache),
41405                         note: cloneCache(_noteCache),
41406                         user: cloneCache(_userCache)
41407                     };
41408                 }
41409
41410                 // access caches directly for testing (e.g., loading notes rtree)
41411                 if (obj === 'get') {
41412                     return {
41413                         tile: _tileCache,
41414                         note: _noteCache,
41415                         user: _userCache
41416                     };
41417                 }
41418
41419                 if (obj.tile) {
41420                     _tileCache = obj.tile;
41421                     _tileCache.inflight = {};
41422                 }
41423                 if (obj.note) {
41424                     _noteCache = obj.note;
41425                     _noteCache.inflight = {};
41426                     _noteCache.inflightPost = {};
41427                 }
41428                 if (obj.user) {
41429                     _userCache = obj.user;
41430                 }
41431
41432                 return this;
41433             },
41434
41435
41436             logout: function() {
41437                 _userChangesets = undefined;
41438                 _userDetails = undefined;
41439                 oauth.logout();
41440                 dispatch$6.call('change');
41441                 return this;
41442             },
41443
41444
41445             authenticated: function() {
41446                 return oauth.authenticated();
41447             },
41448
41449
41450             authenticate: function(callback) {
41451                 var that = this;
41452                 var cid = _connectionID;
41453                 _userChangesets = undefined;
41454                 _userDetails = undefined;
41455
41456                 function done(err, res) {
41457                     if (err) {
41458                         if (callback) { callback(err); }
41459                         return;
41460                     }
41461                     if (that.getConnectionId() !== cid) {
41462                         if (callback) { callback({ message: 'Connection Switched', status: -1 }); }
41463                         return;
41464                     }
41465                     _rateLimitError = undefined;
41466                     dispatch$6.call('change');
41467                     if (callback) { callback(err, res); }
41468                     that.userChangesets(function() {});  // eagerly load user details/changesets
41469                 }
41470
41471                 return oauth.authenticate(done);
41472             },
41473
41474
41475             imageryBlocklists: function() {
41476                 return _imageryBlocklists;
41477             },
41478
41479
41480             tileZoom: function(val) {
41481                 if (!arguments.length) { return _tileZoom$3; }
41482                 _tileZoom$3 = val;
41483                 return this;
41484             },
41485
41486
41487             // get all cached notes covering the viewport
41488             notes: function(projection) {
41489                 var viewport = projection.clipExtent();
41490                 var min = [viewport[0][0], viewport[1][1]];
41491                 var max = [viewport[1][0], viewport[0][1]];
41492                 var bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox();
41493
41494                 return _noteCache.rtree.search(bbox)
41495                     .map(function(d) { return d.data; });
41496             },
41497
41498
41499             // get a single note from the cache
41500             getNote: function(id) {
41501                 return _noteCache.note[id];
41502             },
41503
41504
41505             // remove a single note from the cache
41506             removeNote: function(note) {
41507                 if (!(note instanceof osmNote) || !note.id) { return; }
41508
41509                 delete _noteCache.note[note.id];
41510                 updateRtree$3(encodeNoteRtree(note), false);  // false = remove
41511             },
41512
41513
41514             // replace a single note in the cache
41515             replaceNote: function(note) {
41516                 if (!(note instanceof osmNote) || !note.id) { return; }
41517
41518                 _noteCache.note[note.id] = note;
41519                 updateRtree$3(encodeNoteRtree(note), true);  // true = replace
41520                 return note;
41521             },
41522
41523
41524             // Get an array of note IDs closed during this session.
41525             // Used to populate `closed:note` changeset tag
41526             getClosedIDs: function() {
41527                 return Object.keys(_noteCache.closed).sort();
41528             }
41529
41530         };
41531
41532         var apibase$3 = 'https://wiki.openstreetmap.org/w/api.php';
41533         var _inflight$1 = {};
41534         var _wikibaseCache = {};
41535         var _localeIDs = { en: false };
41536
41537
41538         var debouncedRequest = debounce(request, 500, { leading: false });
41539
41540         function request(url, callback) {
41541             if (_inflight$1[url]) { return; }
41542             var controller = new AbortController();
41543             _inflight$1[url] = controller;
41544
41545             d3_json(url, { signal: controller.signal })
41546                 .then(function(result) {
41547                     delete _inflight$1[url];
41548                     if (callback) { callback(null, result); }
41549                 })
41550                 .catch(function(err) {
41551                     delete _inflight$1[url];
41552                     if (err.name === 'AbortError') { return; }
41553                     if (callback) { callback(err.message); }
41554                 });
41555         }
41556
41557
41558         /**
41559          * Get the best string value from the descriptions/labels result
41560          * Note that if mediawiki doesn't recognize language code, it will return all values.
41561          * In that case, fallback to use English.
41562          * @param values object - either descriptions or labels
41563          * @param langCode String
41564          * @returns localized string
41565          */
41566         function localizedToString(values, langCode) {
41567             if (values) {
41568                 values = values[langCode] || values.en;
41569             }
41570             return values ? values.value : '';
41571         }
41572
41573
41574         var serviceOsmWikibase = {
41575
41576             init: function() {
41577                 _inflight$1 = {};
41578                 _wikibaseCache = {};
41579                 _localeIDs = {};
41580             },
41581
41582
41583             reset: function() {
41584                 Object.values(_inflight$1).forEach(function(controller) { controller.abort(); });
41585                 _inflight$1 = {};
41586             },
41587
41588
41589             /**
41590              * Get the best value for the property, or undefined if not found
41591              * @param entity object from wikibase
41592              * @param property string e.g. 'P4' for image
41593              * @param langCode string e.g. 'fr' for French
41594              */
41595             claimToValue: function(entity, property, langCode) {
41596                 if (!entity.claims[property]) { return undefined; }
41597                 var locale = _localeIDs[langCode];
41598                 var preferredPick, localePick;
41599
41600                 entity.claims[property].forEach(function(stmt) {
41601                     // If exists, use value limited to the needed language (has a qualifier P26 = locale)
41602                     // Or if not found, use the first value with the "preferred" rank
41603                     if (!preferredPick && stmt.rank === 'preferred') {
41604                         preferredPick = stmt;
41605                     }
41606                     if (locale && stmt.qualifiers && stmt.qualifiers.P26 &&
41607                         stmt.qualifiers.P26[0].datavalue.value.id === locale
41608                     ) {
41609                         localePick = stmt;
41610                     }
41611                 });
41612
41613                 var result = localePick || preferredPick;
41614                 if (result) {
41615                     var datavalue = result.mainsnak.datavalue;
41616                     return datavalue.type === 'wikibase-entityid' ? datavalue.value.id : datavalue.value;
41617                 } else {
41618                     return undefined;
41619                 }
41620             },
41621
41622
41623             /**
41624              * Convert monolingual property into a key-value object (language -> value)
41625              * @param entity object from wikibase
41626              * @param property string e.g. 'P31' for monolingual wiki page title
41627              */
41628             monolingualClaimToValueObj: function(entity, property) {
41629                 if (!entity || !entity.claims[property]) { return undefined; }
41630
41631                 return entity.claims[property].reduce(function(acc, obj) {
41632                     var value = obj.mainsnak.datavalue.value;
41633                     acc[value.language] = value.text;
41634                     return acc;
41635                 }, {});
41636             },
41637
41638
41639             toSitelink: function(key, value) {
41640                 var result = value ? ('Tag:' + key + '=' + value) : 'Key:' + key;
41641                 return result.replace(/_/g, ' ').trim();
41642             },
41643
41644
41645             //
41646             // Pass params object of the form:
41647             // {
41648             //   key: 'string',
41649             //   value: 'string',
41650             //   langCode: 'string'
41651             // }
41652             //
41653             getEntity: function(params, callback) {
41654                 var doRequest = params.debounce ? debouncedRequest : request;
41655                 var that = this;
41656                 var titles = [];
41657                 var result = {};
41658                 var rtypeSitelink = (params.key === 'type' && params.value) ? ('Relation:' + params.value).replace(/_/g, ' ').trim() : false;
41659                 var keySitelink = params.key ? this.toSitelink(params.key) : false;
41660                 var tagSitelink = (params.key && params.value) ? this.toSitelink(params.key, params.value) : false;
41661                 var localeSitelink;
41662
41663                 if (params.langCode && _localeIDs[params.langCode] === undefined) {
41664                     // If this is the first time we are asking about this locale,
41665                     // fetch corresponding entity (if it exists), and cache it.
41666                     // If there is no such entry, cache `false` value to avoid re-requesting it.
41667                     localeSitelink = ('Locale:' + params.langCode).replace(/_/g, ' ').trim();
41668                     titles.push(localeSitelink);
41669                 }
41670
41671                 if (rtypeSitelink) {
41672                     if (_wikibaseCache[rtypeSitelink]) {
41673                         result.rtype = _wikibaseCache[rtypeSitelink];
41674                     } else {
41675                         titles.push(rtypeSitelink);
41676                     }
41677                 }
41678
41679                 if (keySitelink) {
41680                     if (_wikibaseCache[keySitelink]) {
41681                         result.key = _wikibaseCache[keySitelink];
41682                     } else {
41683                         titles.push(keySitelink);
41684                     }
41685                 }
41686
41687                 if (tagSitelink) {
41688                     if (_wikibaseCache[tagSitelink]) {
41689                         result.tag = _wikibaseCache[tagSitelink];
41690                     } else {
41691                         titles.push(tagSitelink);
41692                     }
41693                 }
41694
41695                 if (!titles.length) {
41696                     // Nothing to do, we already had everything in the cache
41697                     return callback(null, result);
41698                 }
41699
41700                 // Requesting just the user language code
41701                 // If backend recognizes the code, it will perform proper fallbacks,
41702                 // and the result will contain the requested code. If not, all values are returned:
41703                 // {"zh-tw":{"value":"...","language":"zh-tw","source-language":"zh-hant"}
41704                 // {"pt-br":{"value":"...","language":"pt","for-language":"pt-br"}}
41705                 var obj = {
41706                     action: 'wbgetentities',
41707                     sites: 'wiki',
41708                     titles: titles.join('|'),
41709                     languages: params.langCode,
41710                     languagefallback: 1,
41711                     origin: '*',
41712                     format: 'json',
41713                     // There is an MW Wikibase API bug https://phabricator.wikimedia.org/T212069
41714                     // We shouldn't use v1 until it gets fixed, but should switch to it afterwards
41715                     // formatversion: 2,
41716                 };
41717
41718                 var url = apibase$3 + '?' + utilQsString(obj);
41719                 doRequest(url, function(err, d) {
41720                     if (err) {
41721                         callback(err);
41722                     } else if (!d.success || d.error) {
41723                         callback(d.error.messages.map(function(v) { return v.html['*']; }).join('<br>'));
41724                     } else {
41725                         var localeID = false;
41726                         Object.values(d.entities).forEach(function(res) {
41727                             if (res.missing !== '') {
41728                                 // Simplify access to the localized values
41729                                 res.description = localizedToString(res.descriptions, params.langCode);
41730                                 res.label = localizedToString(res.labels, params.langCode);
41731
41732                                 var title = res.sitelinks.wiki.title;
41733                                 if (title === rtypeSitelink) {
41734                                     _wikibaseCache[rtypeSitelink] = res;
41735                                     result.rtype = res;
41736                                 } else if (title === keySitelink) {
41737                                     _wikibaseCache[keySitelink] = res;
41738                                     result.key = res;
41739                                 } else if (title === tagSitelink) {
41740                                     _wikibaseCache[tagSitelink] = res;
41741                                     result.tag = res;
41742                                 } else if (title === localeSitelink) {
41743                                     localeID = res.id;
41744                                 } else {
41745                                     console.log('Unexpected title ' + title);  // eslint-disable-line no-console
41746                                 }
41747                             }
41748                         });
41749
41750                         if (localeSitelink) {
41751                             // If locale ID is not found, store false to prevent repeated queries
41752                             that.addLocale(params.langCode, localeID);
41753                         }
41754
41755                         callback(null, result);
41756                     }
41757                 });
41758             },
41759
41760
41761             //
41762             // Pass params object of the form:
41763             // {
41764             //   key: 'string',     // required
41765             //   value: 'string'    // optional
41766             // }
41767             //
41768             // Get an result object used to display tag documentation
41769             // {
41770             //   title:        'string',
41771             //   description:  'string',
41772             //   editURL:      'string',
41773             //   imageURL:     'string',
41774             //   wiki:         { title: 'string', text: 'string', url: 'string' }
41775             // }
41776             //
41777             getDocs: function(params, callback) {
41778                 var that = this;
41779                 var langCode = _mainLocalizer.localeCode().toLowerCase();
41780                 params.langCode = langCode;
41781
41782                 this.getEntity(params, function(err, data) {
41783                     if (err) {
41784                         callback(err);
41785                         return;
41786                     }
41787
41788                     var entity = data.rtype || data.tag || data.key;
41789                     if (!entity) {
41790                         callback('No entity');
41791                         return;
41792                     }
41793
41794                     // prepare result
41795                     var result = {
41796                         title: entity.title,
41797                         description: entity.description,
41798                         editURL: 'https://wiki.openstreetmap.org/wiki/' + entity.title
41799                     };
41800
41801                     // add image
41802                     if (entity.claims) {
41803                         var imageroot;
41804                         var image = that.claimToValue(entity, 'P4', langCode);
41805                         if (image) {
41806                             imageroot = 'https://commons.wikimedia.org/w/index.php';
41807                         } else {
41808                             image = that.claimToValue(entity, 'P28', langCode);
41809                             if (image) {
41810                                 imageroot = 'https://wiki.openstreetmap.org/w/index.php';
41811                             }
41812                         }
41813                         if (imageroot && image) {
41814                             result.imageURL = imageroot + '?' + utilQsString({
41815                                 title: 'Special:Redirect/file/' + image,
41816                                 width: 400
41817                             });
41818                         }
41819                     }
41820
41821                     // Try to get a wiki page from tag data item first, followed by the corresponding key data item.
41822                     // If neither tag nor key data item contain a wiki page in the needed language nor English,
41823                     // get the first found wiki page from either the tag or the key item.
41824                     var rtypeWiki = that.monolingualClaimToValueObj(data.rtype, 'P31');
41825                     var tagWiki = that.monolingualClaimToValueObj(data.tag, 'P31');
41826                     var keyWiki = that.monolingualClaimToValueObj(data.key, 'P31');
41827
41828                     // If exact language code does not exist, try to find the first part before the '-'
41829                     // BUG: in some cases, a more elaborate fallback logic might be needed
41830                     var langPrefix = langCode.split('-', 2)[0];
41831
41832                     // use the first acceptable wiki page
41833                     result.wiki =
41834                         getWikiInfo(rtypeWiki, langCode, 'inspector.wiki_reference') ||
41835                         getWikiInfo(rtypeWiki, langPrefix, 'inspector.wiki_reference') ||
41836                         getWikiInfo(rtypeWiki, 'en', 'inspector.wiki_en_reference') ||
41837                         getWikiInfo(tagWiki, langCode, 'inspector.wiki_reference') ||
41838                         getWikiInfo(tagWiki, langPrefix, 'inspector.wiki_reference') ||
41839                         getWikiInfo(tagWiki, 'en', 'inspector.wiki_en_reference') ||
41840                         getWikiInfo(keyWiki, langCode, 'inspector.wiki_reference') ||
41841                         getWikiInfo(keyWiki, langPrefix, 'inspector.wiki_reference') ||
41842                         getWikiInfo(keyWiki, 'en', 'inspector.wiki_en_reference');
41843
41844                     callback(null, result);
41845
41846
41847                     // Helper method to get wiki info if a given language exists
41848                     function getWikiInfo(wiki, langCode, tKey) {
41849                         if (wiki && wiki[langCode]) {
41850                             return {
41851                                 title: wiki[langCode],
41852                                 text: tKey,
41853                                 url: 'https://wiki.openstreetmap.org/wiki/' + wiki[langCode]
41854                             };
41855                         }
41856                     }
41857                 });
41858             },
41859
41860
41861             addLocale: function(langCode, qid) {
41862                 // Makes it easier to unit test
41863                 _localeIDs[langCode] = qid;
41864             },
41865
41866
41867             apibase: function(val) {
41868                 if (!arguments.length) { return apibase$3; }
41869                 apibase$3 = val;
41870                 return this;
41871             }
41872
41873         };
41874
41875         var jsonpCache = {};
41876         window.jsonpCache = jsonpCache;
41877
41878         function jsonpRequest(url, callback) {
41879             var request = {
41880                 abort: function() {}
41881             };
41882
41883             if (window.JSONP_FIX) {
41884                 if (window.JSONP_DELAY === 0) {
41885                     callback(window.JSONP_FIX);
41886                 } else {
41887                     var t = window.setTimeout(function() {
41888                         callback(window.JSONP_FIX);
41889                     }, window.JSONP_DELAY || 0);
41890
41891                     request.abort = function() { window.clearTimeout(t); };
41892                 }
41893
41894                 return request;
41895             }
41896
41897             function rand() {
41898                 var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
41899                 var c = '';
41900                 var i = -1;
41901                 while (++i < 15) { c += chars.charAt(Math.floor(Math.random() * 52)); }
41902                 return c;
41903             }
41904
41905             function create(url) {
41906                 var e = url.match(/callback=(\w+)/);
41907                 var c = e ? e[1] : rand();
41908
41909                 jsonpCache[c] = function(data) {
41910                     if (jsonpCache[c]) {
41911                         callback(data);
41912                     }
41913                     finalize();
41914                 };
41915
41916                 function finalize() {
41917                     delete jsonpCache[c];
41918                     script.remove();
41919                 }
41920
41921                 request.abort = finalize;
41922                 return 'jsonpCache.' + c;
41923             }
41924
41925             var cb = create(url);
41926
41927             var script = select('head')
41928                 .append('script')
41929                 .attr('type', 'text/javascript')
41930                 .attr('src', url.replace(/(\{|%7B)callback(\}|%7D)/, cb));
41931
41932             return request;
41933         }
41934
41935         var bubbleApi = 'https://dev.virtualearth.net/mapcontrol/HumanScaleServices/GetBubbles.ashx?';
41936         var streetsideImagesApi = 'https://t.ssl.ak.tiles.virtualearth.net/tiles/';
41937         var bubbleAppKey = 'AuftgJsO0Xs8Ts4M1xZUQJQXJNsvmh3IV8DkNieCiy3tCwCUMq76-WpkrBtNAuEm';
41938         var pannellumViewerCSS = 'pannellum-streetside/pannellum.css';
41939         var pannellumViewerJS = 'pannellum-streetside/pannellum.js';
41940         var maxResults$2 = 2000;
41941         var tileZoom$2 = 16.5;
41942         var tiler$6 = utilTiler().zoomExtent([tileZoom$2, tileZoom$2]).skipNullIsland(true);
41943         var dispatch$7 = dispatch('loadedBubbles', 'viewerChanged');
41944         var minHfov = 10;         // zoom in degrees:  20, 10, 5
41945         var maxHfov = 90;         // zoom out degrees
41946         var defaultHfov = 45;
41947
41948         var _hires = false;
41949         var _resolution = 512;    // higher numbers are slower - 512, 1024, 2048, 4096
41950         var _currScene = 0;
41951         var _ssCache;
41952         var _pannellumViewer;
41953         var _sceneOptions;
41954         var _dataUrlArray = [];
41955
41956
41957         /**
41958          * abortRequest().
41959          */
41960         function abortRequest$6(i) {
41961           i.abort();
41962         }
41963
41964
41965         /**
41966          * localeTimeStamp().
41967          */
41968         function localeTimestamp(s) {
41969           if (!s) { return null; }
41970           var options = { day: 'numeric', month: 'short', year: 'numeric' };
41971           var d = new Date(s);
41972           if (isNaN(d.getTime())) { return null; }
41973           return d.toLocaleString(_mainLocalizer.localeCode(), options);
41974         }
41975
41976
41977         /**
41978          * loadTiles() wraps the process of generating tiles and then fetching image points for each tile.
41979          */
41980         function loadTiles$2(which, url, projection, margin) {
41981           var tiles = tiler$6.margin(margin).getTiles(projection);
41982
41983           // abort inflight requests that are no longer needed
41984           var cache = _ssCache[which];
41985           Object.keys(cache.inflight).forEach(function (k) {
41986             var wanted = tiles.find(function (tile) { return k.indexOf(tile.id + ',') === 0; });
41987             if (!wanted) {
41988               abortRequest$6(cache.inflight[k]);
41989               delete cache.inflight[k];
41990             }
41991           });
41992
41993           tiles.forEach(function (tile) { return loadNextTilePage$2(which, url, tile); });
41994         }
41995
41996
41997         /**
41998          * loadNextTilePage() load data for the next tile page in line.
41999          */
42000         function loadNextTilePage$2(which, url, tile) {
42001           var cache = _ssCache[which];
42002           var nextPage = cache.nextPage[tile.id] || 0;
42003           var id = tile.id + ',' + String(nextPage);
42004           if (cache.loaded[id] || cache.inflight[id]) { return; }
42005
42006           cache.inflight[id] = getBubbles(url, tile, function (bubbles) {
42007             cache.loaded[id] = true;
42008             delete cache.inflight[id];
42009             if (!bubbles) { return; }
42010
42011             // [].shift() removes the first element, some statistics info, not a bubble point
42012             bubbles.shift();
42013
42014             var features = bubbles.map(function (bubble) {
42015               if (cache.points[bubble.id]) { return null; }  // skip duplicates
42016
42017               var loc = [bubble.lo, bubble.la];
42018               var d = {
42019                 loc: loc,
42020                 key: bubble.id,
42021                 ca: bubble.he,
42022                 captured_at: bubble.cd,
42023                 captured_by: 'microsoft',
42024                 // nbn: bubble.nbn,
42025                 // pbn: bubble.pbn,
42026                 // ad: bubble.ad,
42027                 // rn: bubble.rn,
42028                 pr: bubble.pr,  // previous
42029                 ne: bubble.ne,  // next
42030                 pano: true,
42031                 sequenceKey: null
42032               };
42033
42034               cache.points[bubble.id] = d;
42035
42036               // a sequence starts here
42037               if (bubble.pr === undefined) {
42038                 cache.leaders.push(bubble.id);
42039               }
42040
42041               return {
42042                 minX: loc[0], minY: loc[1], maxX: loc[0], maxY: loc[1], data: d
42043               };
42044
42045             }).filter(Boolean);
42046
42047             cache.rtree.load(features);
42048
42049             connectSequences();
42050
42051             if (which === 'bubbles') {
42052               dispatch$7.call('loadedBubbles');
42053             }
42054           });
42055         }
42056
42057
42058         // call this sometimes to connect the bubbles into sequences
42059         function connectSequences() {
42060           var cache = _ssCache.bubbles;
42061           var keepLeaders = [];
42062
42063           for (var i = 0; i < cache.leaders.length; i++) {
42064             var bubble = cache.points[cache.leaders[i]];
42065             var seen = {};
42066
42067             // try to make a sequence.. use the key of the leader bubble.
42068             var sequence = { key: bubble.key, bubbles: [] };
42069             var complete = false;
42070
42071             do {
42072               sequence.bubbles.push(bubble);
42073               seen[bubble.key] = true;
42074
42075               if (bubble.ne === undefined) {
42076                 complete = true;
42077               } else {
42078                 bubble = cache.points[bubble.ne];  // advance to next
42079               }
42080             } while (bubble && !seen[bubble.key] && !complete);
42081
42082
42083             if (complete) {
42084               _ssCache.sequences[sequence.key] = sequence;
42085
42086               // assign bubbles to the sequence
42087               for (var j = 0; j < sequence.bubbles.length; j++) {
42088                 sequence.bubbles[j].sequenceKey = sequence.key;
42089               }
42090
42091               // create a GeoJSON LineString
42092               sequence.geojson = {
42093                 type: 'LineString',
42094                 properties: { key: sequence.key },
42095                 coordinates: sequence.bubbles.map(function (d) { return d.loc; })
42096               };
42097
42098             } else {
42099               keepLeaders.push(cache.leaders[i]);
42100             }
42101           }
42102
42103           // couldn't complete these, save for later
42104           cache.leaders = keepLeaders;
42105         }
42106
42107
42108         /**
42109          * getBubbles() handles the request to the server for a tile extent of 'bubbles' (streetside image locations).
42110          */
42111         function getBubbles(url, tile, callback) {
42112           var rect = tile.extent.rectangle();
42113           var urlForRequest = url + utilQsString({
42114             n: rect[3],
42115             s: rect[1],
42116             e: rect[2],
42117             w: rect[0],
42118             c: maxResults$2,
42119             appkey: bubbleAppKey,
42120             jsCallback: '{callback}'
42121           });
42122
42123           return jsonpRequest(urlForRequest, function (data) {
42124             if (!data || data.error) {
42125               callback(null);
42126             } else {
42127               callback(data);
42128             }
42129           });
42130         }
42131
42132
42133         // partition viewport into higher zoom tiles
42134         function partitionViewport$2(projection) {
42135           var z = geoScaleToZoom(projection.scale());
42136           var z2 = (Math.ceil(z * 2) / 2) + 2.5;   // round to next 0.5 and add 2.5
42137           var tiler = utilTiler().zoomExtent([z2, z2]);
42138
42139           return tiler.getTiles(projection)
42140             .map(function (tile) { return tile.extent; });
42141         }
42142
42143
42144         // no more than `limit` results per partition.
42145         function searchLimited$2(limit, projection, rtree) {
42146           limit = limit || 5;
42147
42148           return partitionViewport$2(projection)
42149             .reduce(function (result, extent) {
42150               var found = rtree.search(extent.bbox())
42151                 .slice(0, limit)
42152                 .map(function (d) { return d.data; });
42153
42154               return (found.length ? result.concat(found) : result);
42155             }, []);
42156         }
42157
42158
42159         /**
42160          * loadImage()
42161          */
42162         function loadImage(imgInfo) {
42163           return new Promise(function (resolve) {
42164             var img = new Image();
42165             img.onload = function () {
42166               var canvas = document.getElementById('ideditor-canvas' + imgInfo.face);
42167               var ctx = canvas.getContext('2d');
42168               ctx.drawImage(img, imgInfo.x, imgInfo.y);
42169               resolve({ imgInfo: imgInfo, status: 'ok' });
42170             };
42171             img.onerror = function () {
42172               resolve({ data: imgInfo, status: 'error' });
42173             };
42174             img.setAttribute('crossorigin', '');
42175             img.src = imgInfo.url;
42176           });
42177         }
42178
42179
42180         /**
42181          * loadCanvas()
42182          */
42183         function loadCanvas(imageGroup) {
42184           return Promise.all(imageGroup.map(loadImage))
42185             .then(function (data) {
42186               var canvas = document.getElementById('ideditor-canvas' + data[0].imgInfo.face);
42187               var which = { '01': 0, '02': 1, '03': 2, '10': 3, '11': 4, '12': 5 };
42188               var face = data[0].imgInfo.face;
42189               _dataUrlArray[which[face]] = canvas.toDataURL('image/jpeg', 1.0);
42190               return { status: 'loadCanvas for face ' + data[0].imgInfo.face + 'ok'};
42191             });
42192         }
42193
42194
42195         /**
42196          * loadFaces()
42197          */
42198         function loadFaces(faceGroup) {
42199           return Promise.all(faceGroup.map(loadCanvas))
42200             .then(function () { return { status: 'loadFaces done' }; });
42201         }
42202
42203
42204         function setupCanvas(selection, reset) {
42205           if (reset) {
42206             selection.selectAll('#ideditor-stitcher-canvases')
42207               .remove();
42208           }
42209
42210           // Add the Streetside working canvases. These are used for 'stitching', or combining,
42211           // multiple images for each of the six faces, before passing to the Pannellum control as DataUrls
42212           selection.selectAll('#ideditor-stitcher-canvases')
42213             .data([0])
42214             .enter()
42215             .append('div')
42216             .attr('id', 'ideditor-stitcher-canvases')
42217             .attr('display', 'none')
42218             .selectAll('canvas')
42219             .data(['canvas01', 'canvas02', 'canvas03', 'canvas10', 'canvas11', 'canvas12'])
42220             .enter()
42221             .append('canvas')
42222             .attr('id', function (d) { return 'ideditor-' + d; })
42223             .attr('width', _resolution)
42224             .attr('height', _resolution);
42225         }
42226
42227
42228         function qkToXY(qk) {
42229           var x = 0;
42230           var y = 0;
42231           var scale = 256;
42232           for (var i = qk.length; i > 0; i--) {
42233             var key = qk[i-1];
42234             x += (+(key === '1' || key === '3')) * scale;
42235             y += (+(key === '2' || key === '3')) * scale;
42236             scale *= 2;
42237           }
42238           return [x, y];
42239         }
42240
42241
42242         function getQuadKeys() {
42243           var dim = _resolution / 256;
42244           var quadKeys;
42245
42246           if (dim === 16) {
42247             quadKeys = [
42248               '0000','0001','0010','0011','0100','0101','0110','0111',  '1000','1001','1010','1011','1100','1101','1110','1111',
42249               '0002','0003','0012','0013','0102','0103','0112','0113',  '1002','1003','1012','1013','1102','1103','1112','1113',
42250               '0020','0021','0030','0031','0120','0121','0130','0131',  '1020','1021','1030','1031','1120','1121','1130','1131',
42251               '0022','0023','0032','0033','0122','0123','0132','0133',  '1022','1023','1032','1033','1122','1123','1132','1133',
42252               '0200','0201','0210','0211','0300','0301','0310','0311',  '1200','1201','1210','1211','1300','1301','1310','1311',
42253               '0202','0203','0212','0213','0302','0303','0312','0313',  '1202','1203','1212','1213','1302','1303','1312','1313',
42254               '0220','0221','0230','0231','0320','0321','0330','0331',  '1220','1221','1230','1231','1320','1321','1330','1331',
42255               '0222','0223','0232','0233','0322','0323','0332','0333',  '1222','1223','1232','1233','1322','1323','1332','1333',
42256
42257               '2000','2001','2010','2011','2100','2101','2110','2111',  '3000','3001','3010','3011','3100','3101','3110','3111',
42258               '2002','2003','2012','2013','2102','2103','2112','2113',  '3002','3003','3012','3013','3102','3103','3112','3113',
42259               '2020','2021','2030','2031','2120','2121','2130','2131',  '3020','3021','3030','3031','3120','3121','3130','3131',
42260               '2022','2023','2032','2033','2122','2123','2132','2133',  '3022','3023','3032','3033','3122','3123','3132','3133',
42261               '2200','2201','2210','2211','2300','2301','2310','2311',  '3200','3201','3210','3211','3300','3301','3310','3311',
42262               '2202','2203','2212','2213','2302','2303','2312','2313',  '3202','3203','3212','3213','3302','3303','3312','3313',
42263               '2220','2221','2230','2231','2320','2321','2330','2331',  '3220','3221','3230','3231','3320','3321','3330','3331',
42264               '2222','2223','2232','2233','2322','2323','2332','2333',  '3222','3223','3232','3233','3322','3323','3332','3333'
42265             ];
42266
42267           } else if (dim === 8) {
42268             quadKeys = [
42269               '000','001','010','011',  '100','101','110','111',
42270               '002','003','012','013',  '102','103','112','113',
42271               '020','021','030','031',  '120','121','130','131',
42272               '022','023','032','033',  '122','123','132','133',
42273
42274               '200','201','210','211',  '300','301','310','311',
42275               '202','203','212','213',  '302','303','312','313',
42276               '220','221','230','231',  '320','321','330','331',
42277               '222','223','232','233',  '322','323','332','333'
42278             ];
42279
42280           } else if (dim === 4) {
42281             quadKeys = [
42282               '00','01',  '10','11',
42283               '02','03',  '12','13',
42284
42285               '20','21',  '30','31',
42286               '22','23',  '32','33'
42287             ];
42288
42289           } else {  // dim === 2
42290             quadKeys = [
42291               '0', '1',
42292               '2', '3'
42293             ];
42294           }
42295
42296           return quadKeys;
42297         }
42298
42299
42300
42301         var serviceStreetside = {
42302           /**
42303            * init() initialize streetside.
42304            */
42305           init: function() {
42306             if (!_ssCache) {
42307               this.reset();
42308             }
42309
42310             this.event = utilRebind(this, dispatch$7, 'on');
42311           },
42312
42313           /**
42314            * reset() reset the cache.
42315            */
42316           reset: function() {
42317             if (_ssCache) {
42318               Object.values(_ssCache.bubbles.inflight).forEach(abortRequest$6);
42319             }
42320
42321             _ssCache = {
42322               bubbles: { inflight: {}, loaded: {}, nextPage: {}, rtree: new RBush(), points: {}, leaders: [] },
42323               sequences: {}
42324             };
42325           },
42326
42327           /**
42328            * bubbles()
42329            */
42330           bubbles: function(projection) {
42331             var limit = 5;
42332             return searchLimited$2(limit, projection, _ssCache.bubbles.rtree);
42333           },
42334
42335
42336           sequences: function(projection) {
42337             var viewport = projection.clipExtent();
42338             var min = [viewport[0][0], viewport[1][1]];
42339             var max = [viewport[1][0], viewport[0][1]];
42340             var bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox();
42341             var seen = {};
42342             var results = [];
42343
42344             // all sequences for bubbles in viewport
42345             _ssCache.bubbles.rtree.search(bbox)
42346               .forEach(function (d) {
42347                 var key = d.data.sequenceKey;
42348                 if (key && !seen[key]) {
42349                     seen[key] = true;
42350                     results.push(_ssCache.sequences[key].geojson);
42351                 }
42352               });
42353
42354             return results;
42355           },
42356
42357
42358           /**
42359            * loadBubbles()
42360            */
42361           loadBubbles: function(projection, margin) {
42362             // by default: request 2 nearby tiles so we can connect sequences.
42363             if (margin === undefined) { margin = 2; }
42364
42365             loadTiles$2('bubbles', bubbleApi, projection, margin);
42366           },
42367
42368
42369           viewer: function() {
42370             return _pannellumViewer;
42371           },
42372
42373
42374           initViewer: function () {
42375             if (!window.pannellum) { return; }
42376             if (_pannellumViewer) { return; }
42377
42378             var sceneID = ++_currScene + '';
42379             var options = {
42380               'default': { firstScene: sceneID },
42381               scenes: {}
42382             };
42383             options.scenes[sceneID] = _sceneOptions;
42384
42385             _pannellumViewer = window.pannellum.viewer('ideditor-viewer-streetside', options);
42386           },
42387
42388
42389           /**
42390            * loadViewer() create the streeside viewer.
42391            */
42392           loadViewer: function(context) {
42393             var that = this;
42394
42395             var pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
42396
42397             // create ms-wrapper, a photo wrapper class
42398             var wrap = context.container().select('.photoviewer').selectAll('.ms-wrapper')
42399               .data([0]);
42400
42401             // inject ms-wrapper into the photoviewer div
42402             // (used by all to house each custom photo viewer)
42403             var wrapEnter = wrap.enter()
42404               .append('div')
42405               .attr('class', 'photo-wrapper ms-wrapper')
42406               .classed('hide', true);
42407
42408             // inject div to support streetside viewer (pannellum) and attribution line
42409             wrapEnter
42410               .append('div')
42411               .attr('id', 'ideditor-viewer-streetside')
42412               .on(pointerPrefix + 'down.streetside', function () {
42413                 select(window)
42414                   .on(pointerPrefix + 'move.streetside', function () {
42415                     dispatch$7.call('viewerChanged');
42416                   }, true);
42417               })
42418               .on(pointerPrefix + 'up.streetside pointercancel.streetside', function () {
42419                 select(window)
42420                   .on(pointerPrefix + 'move.streetside', null);
42421
42422                 // continue dispatching events for a few seconds, in case viewer has inertia.
42423                 var t = timer(function (elapsed) {
42424                   dispatch$7.call('viewerChanged');
42425                   if (elapsed > 2000) {
42426                     t.stop();
42427                   }
42428                 });
42429               })
42430               .append('div')
42431               .attr('class', 'photo-attribution fillD');
42432
42433             var controlsEnter = wrapEnter
42434               .append('div')
42435               .attr('class', 'photo-controls-wrap')
42436               .append('div')
42437               .attr('class', 'photo-controls');
42438
42439             controlsEnter
42440               .append('button')
42441               .on('click.back', step(-1))
42442               .text('◄');
42443
42444             controlsEnter
42445               .append('button')
42446               .on('click.forward', step(1))
42447               .text('►');
42448
42449
42450             // create working canvas for stitching together images
42451             wrap = wrap
42452               .merge(wrapEnter)
42453               .call(setupCanvas, true);
42454
42455             // load streetside pannellum viewer css
42456             select('head').selectAll('#ideditor-streetside-viewercss')
42457               .data([0])
42458               .enter()
42459               .append('link')
42460               .attr('id', 'ideditor-streetside-viewercss')
42461               .attr('rel', 'stylesheet')
42462               .attr('href', context.asset(pannellumViewerCSS));
42463
42464             // load streetside pannellum viewer js
42465             select('head').selectAll('#ideditor-streetside-viewerjs')
42466               .data([0])
42467               .enter()
42468               .append('script')
42469               .attr('id', 'ideditor-streetside-viewerjs')
42470               .attr('src', context.asset(pannellumViewerJS));
42471
42472
42473             // Register viewer resize handler
42474             context.ui().photoviewer.on('resize.streetside', function () {
42475               if (_pannellumViewer) {
42476                 _pannellumViewer.resize();
42477               }
42478             });
42479
42480
42481             function step(stepBy) {
42482               return function () {
42483                 var viewer = context.container().select('.photoviewer');
42484                 var selected = viewer.empty() ? undefined : viewer.datum();
42485                 if (!selected) { return; }
42486
42487                 var nextID = (stepBy === 1 ? selected.ne : selected.pr);
42488                 var yaw = _pannellumViewer.getYaw();
42489                 var ca = selected.ca + yaw;
42490                 var origin = selected.loc;
42491
42492                 // construct a search trapezoid pointing out from current bubble
42493                 var meters = 35;
42494                 var p1 = [
42495                   origin[0] + geoMetersToLon(meters / 5, origin[1]),
42496                   origin[1]
42497                 ];
42498                 var p2 = [
42499                   origin[0] + geoMetersToLon(meters / 2, origin[1]),
42500                   origin[1] + geoMetersToLat(meters)
42501                 ];
42502                 var p3 = [
42503                   origin[0] - geoMetersToLon(meters / 2, origin[1]),
42504                   origin[1] + geoMetersToLat(meters)
42505                 ];
42506                 var p4 = [
42507                   origin[0] - geoMetersToLon(meters / 5, origin[1]),
42508                   origin[1]
42509                 ];
42510
42511                 var poly = [p1, p2, p3, p4, p1];
42512
42513                 // rotate it to face forward/backward
42514                 var angle = (stepBy === 1 ? ca : ca + 180) * (Math.PI / 180);
42515                 poly = geoRotate(poly, -angle, origin);
42516
42517                 var extent = poly.reduce(function (extent, point) {
42518                   return extent.extend(geoExtent(point));
42519                 }, geoExtent());
42520
42521                 // find nearest other bubble in the search polygon
42522                 var minDist = Infinity;
42523                 _ssCache.bubbles.rtree.search(extent.bbox())
42524                   .forEach(function (d) {
42525                     if (d.data.key === selected.key) { return; }
42526                     if (!geoPointInPolygon(d.data.loc, poly)) { return; }
42527
42528                     var dist = geoVecLength(d.data.loc, selected.loc);
42529                     var theta = selected.ca - d.data.ca;
42530                     var minTheta = Math.min(Math.abs(theta), 360 - Math.abs(theta));
42531                     if (minTheta > 20) {
42532                       dist += 5;  // penalize distance if camera angles don't match
42533                     }
42534
42535                     if (dist < minDist) {
42536                       nextID = d.data.key;
42537                       minDist = dist;
42538                     }
42539                   });
42540
42541                 var nextBubble = nextID && _ssCache.bubbles.points[nextID];
42542                 if (!nextBubble) { return; }
42543
42544                 context.map().centerEase(nextBubble.loc);
42545
42546                 that.selectImage(context, nextBubble)
42547                   .then(function (response) {
42548                     if (response.status === 'ok') {
42549                       _sceneOptions.yaw = yaw;
42550                       that.showViewer(context);
42551                     }
42552                   });
42553               };
42554             }
42555           },
42556
42557
42558           /**
42559            * showViewer()
42560            */
42561           showViewer: function(context, yaw) {
42562             if (!_sceneOptions) { return; }
42563
42564             if (yaw !== undefined) {
42565               _sceneOptions.yaw = yaw;
42566             }
42567
42568             if (!_pannellumViewer) {
42569               this.initViewer();
42570             } else {
42571               // make a new scene
42572               var sceneID = ++_currScene + '';
42573               _pannellumViewer
42574                 .addScene(sceneID, _sceneOptions)
42575                 .loadScene(sceneID);
42576
42577               // remove previous scene
42578               if (_currScene > 2) {
42579                 sceneID = (_currScene - 1) + '';
42580                 _pannellumViewer
42581                   .removeScene(sceneID);
42582               }
42583             }
42584
42585             var wrap = context.container().select('.photoviewer')
42586               .classed('hide', false);
42587
42588             var isHidden = wrap.selectAll('.photo-wrapper.ms-wrapper.hide').size();
42589
42590             if (isHidden) {
42591               wrap
42592                 .selectAll('.photo-wrapper:not(.ms-wrapper)')
42593                 .classed('hide', true);
42594
42595               wrap
42596                 .selectAll('.photo-wrapper.ms-wrapper')
42597                 .classed('hide', false);
42598             }
42599
42600             return this;
42601           },
42602
42603
42604           /**
42605            * hideViewer()
42606            */
42607           hideViewer: function (context) {
42608             var viewer = context.container().select('.photoviewer');
42609             if (!viewer.empty()) { viewer.datum(null); }
42610
42611             viewer
42612               .classed('hide', true)
42613               .selectAll('.photo-wrapper')
42614               .classed('hide', true);
42615
42616             context.container().selectAll('.viewfield-group, .sequence, .icon-sign')
42617               .classed('currentView', false);
42618
42619             return this.setStyles(context, null, true);
42620           },
42621
42622
42623           /**
42624            * selectImage().
42625            */
42626           selectImage: function (context, d) {
42627             var that = this;
42628             var viewer = context.container().select('.photoviewer');
42629             if (!viewer.empty()) { viewer.datum(d); }
42630
42631             this.setStyles(context, null, true);
42632
42633             var wrap = context.container().select('.photoviewer .ms-wrapper');
42634             var attribution = wrap.selectAll('.photo-attribution').html('');
42635
42636             wrap.selectAll('.pnlm-load-box')   // display "loading.."
42637               .style('display', 'block');
42638
42639             if (!d) {
42640               return Promise.resolve({ status: 'ok' });
42641             }
42642
42643             var line1 = attribution
42644               .append('div')
42645               .attr('class', 'attribution-row');
42646
42647             var hiresDomId = utilUniqueDomId('streetside-hires');
42648
42649             // Add hires checkbox
42650             var label = line1
42651               .append('label')
42652               .attr('for', hiresDomId)
42653               .attr('class', 'streetside-hires');
42654
42655             label
42656               .append('input')
42657               .attr('type', 'checkbox')
42658               .attr('id', hiresDomId)
42659               .property('checked', _hires)
42660               .on('click', function () {
42661                 event.stopPropagation();
42662
42663                 _hires = !_hires;
42664                 _resolution = _hires ? 1024 : 512;
42665                 wrap.call(setupCanvas, true);
42666
42667                 var viewstate = {
42668                   yaw: _pannellumViewer.getYaw(),
42669                   pitch: _pannellumViewer.getPitch(),
42670                   hfov: _pannellumViewer.getHfov()
42671                 };
42672
42673                 that.selectImage(context, d)
42674                   .then(function (response) {
42675                     if (response.status === 'ok') {
42676                       _sceneOptions = Object.assign(_sceneOptions, viewstate);
42677                       that.showViewer(context);
42678                     }
42679                   });
42680               });
42681
42682             label
42683               .append('span')
42684               .text(_t('streetside.hires'));
42685
42686
42687             var captureInfo = line1
42688               .append('div')
42689               .attr('class', 'attribution-capture-info');
42690
42691             // Add capture date
42692             if (d.captured_by) {
42693               var yyyy = (new Date()).getFullYear();
42694
42695               captureInfo
42696                 .append('a')
42697                 .attr('class', 'captured_by')
42698                 .attr('target', '_blank')
42699                 .attr('href', 'https://www.microsoft.com/en-us/maps/streetside')
42700                 .text('©' + yyyy + ' Microsoft');
42701
42702               captureInfo
42703                 .append('span')
42704                 .text('|');
42705             }
42706
42707             if (d.captured_at) {
42708               captureInfo
42709                 .append('span')
42710                 .attr('class', 'captured_at')
42711                 .text(localeTimestamp(d.captured_at));
42712             }
42713
42714             // Add image links
42715             var line2 = attribution
42716               .append('div')
42717               .attr('class', 'attribution-row');
42718
42719             line2
42720               .append('a')
42721               .attr('class', 'image-view-link')
42722               .attr('target', '_blank')
42723               .attr('href', 'https://www.bing.com/maps?cp=' + d.loc[1] + '~' + d.loc[0] +
42724                 '&lvl=17&dir=' + d.ca + '&style=x&v=2&sV=1')
42725               .text(_t('streetside.view_on_bing'));
42726
42727             line2
42728               .append('a')
42729               .attr('class', 'image-report-link')
42730               .attr('target', '_blank')
42731               .attr('href', 'https://www.bing.com/maps/privacyreport/streetsideprivacyreport?bubbleid=' +
42732                 encodeURIComponent(d.key) + '&focus=photo&lat=' + d.loc[1] + '&lng=' + d.loc[0] + '&z=17')
42733               .text(_t('streetside.report'));
42734
42735
42736             var bubbleIdQuadKey = d.key.toString(4);
42737             var paddingNeeded = 16 - bubbleIdQuadKey.length;
42738             for (var i = 0; i < paddingNeeded; i++) {
42739               bubbleIdQuadKey = '0' + bubbleIdQuadKey;
42740             }
42741             var imgUrlPrefix = streetsideImagesApi + 'hs' + bubbleIdQuadKey;
42742             var imgUrlSuffix = '.jpg?g=6338&n=z';
42743
42744             // Cubemap face code order matters here: front=01, right=02, back=03, left=10, up=11, down=12
42745             var faceKeys = ['01','02','03','10','11','12'];
42746
42747             // Map images to cube faces
42748             var quadKeys = getQuadKeys();
42749             var faces = faceKeys.map(function (faceKey) {
42750               return quadKeys.map(function (quadKey) {
42751                 var xy = qkToXY(quadKey);
42752                 return {
42753                   face: faceKey,
42754                   url: imgUrlPrefix + faceKey + quadKey + imgUrlSuffix,
42755                   x: xy[0],
42756                   y: xy[1]
42757                 };
42758               });
42759             });
42760
42761             return loadFaces(faces)
42762               .then(function () {
42763                 _sceneOptions = {
42764                   showFullscreenCtrl: false,
42765                   autoLoad: true,
42766                   compass: true,
42767                   northOffset: d.ca,
42768                   yaw: 0,
42769                   minHfov: minHfov,
42770                   maxHfov: maxHfov,
42771                   hfov: defaultHfov,
42772                   type: 'cubemap',
42773                   cubeMap: [
42774                     _dataUrlArray[0],
42775                     _dataUrlArray[1],
42776                     _dataUrlArray[2],
42777                     _dataUrlArray[3],
42778                     _dataUrlArray[4],
42779                     _dataUrlArray[5]
42780                   ]
42781                 };
42782                 return { status: 'ok' };
42783               });
42784           },
42785
42786
42787           getSequenceKeyForBubble: function(d) {
42788             return d && d.sequenceKey;
42789           },
42790
42791
42792           // Updates the currently highlighted sequence and selected bubble.
42793           // Reset is only necessary when interacting with the viewport because
42794           // this implicitly changes the currently selected bubble/sequence
42795           setStyles: function (context, hovered, reset) {
42796             if (reset) {  // reset all layers
42797               context.container().selectAll('.viewfield-group')
42798                 .classed('highlighted', false)
42799                 .classed('hovered', false)
42800                 .classed('currentView', false);
42801
42802               context.container().selectAll('.sequence')
42803                 .classed('highlighted', false)
42804                 .classed('currentView', false);
42805             }
42806
42807             var hoveredBubbleKey = hovered && hovered.key;
42808             var hoveredSequenceKey = this.getSequenceKeyForBubble(hovered);
42809             var hoveredSequence = hoveredSequenceKey && _ssCache.sequences[hoveredSequenceKey];
42810             var hoveredBubbleKeys =  (hoveredSequence && hoveredSequence.bubbles.map(function (d) { return d.key; })) || [];
42811
42812             var viewer = context.container().select('.photoviewer');
42813             var selected = viewer.empty() ? undefined : viewer.datum();
42814             var selectedBubbleKey = selected && selected.key;
42815             var selectedSequenceKey = this.getSequenceKeyForBubble(selected);
42816             var selectedSequence = selectedSequenceKey && _ssCache.sequences[selectedSequenceKey];
42817             var selectedBubbleKeys = (selectedSequence && selectedSequence.bubbles.map(function (d) { return d.key; })) || [];
42818
42819             // highlight sibling viewfields on either the selected or the hovered sequences
42820             var highlightedBubbleKeys = utilArrayUnion(hoveredBubbleKeys, selectedBubbleKeys);
42821
42822             context.container().selectAll('.layer-streetside-images .viewfield-group')
42823               .classed('highlighted', function (d) { return highlightedBubbleKeys.indexOf(d.key) !== -1; })
42824               .classed('hovered',     function (d) { return d.key === hoveredBubbleKey; })
42825               .classed('currentView', function (d) { return d.key === selectedBubbleKey; });
42826
42827             context.container().selectAll('.layer-streetside-images .sequence')
42828               .classed('highlighted', function (d) { return d.properties.key === hoveredSequenceKey; })
42829               .classed('currentView', function (d) { return d.properties.key === selectedSequenceKey; });
42830
42831             // update viewfields if needed
42832             context.container().selectAll('.viewfield-group .viewfield')
42833               .attr('d', viewfieldPath);
42834
42835             function viewfieldPath() {
42836               var d = this.parentNode.__data__;
42837               if (d.pano && d.key !== selectedBubbleKey) {
42838                 return 'M 8,13 m -10,0 a 10,10 0 1,0 20,0 a 10,10 0 1,0 -20,0';
42839               } else {
42840                 return 'M 6,9 C 8,8.4 8,8.4 10,9 L 16,-2 C 12,-5 4,-5 0,-2 z';
42841               }
42842             }
42843
42844             return this;
42845           },
42846
42847
42848           /**
42849            * cache().
42850            */
42851           cache: function () {
42852             return _ssCache;
42853           }
42854         };
42855
42856         var apibase$4 = 'https://taginfo.openstreetmap.org/api/4/';
42857         var _inflight$2 = {};
42858         var _popularKeys = {};
42859         var _taginfoCache = {};
42860
42861         var tag_sorts = {
42862             point: 'count_nodes',
42863             vertex: 'count_nodes',
42864             area: 'count_ways',
42865             line: 'count_ways'
42866         };
42867         var tag_sort_members = {
42868             point: 'count_node_members',
42869             vertex: 'count_node_members',
42870             area: 'count_way_members',
42871             line: 'count_way_members',
42872             relation: 'count_relation_members'
42873         };
42874         var tag_filters = {
42875             point: 'nodes',
42876             vertex: 'nodes',
42877             area: 'ways',
42878             line: 'ways'
42879         };
42880         var tag_members_fractions = {
42881             point: 'count_node_members_fraction',
42882             vertex: 'count_node_members_fraction',
42883             area: 'count_way_members_fraction',
42884             line: 'count_way_members_fraction',
42885             relation: 'count_relation_members_fraction'
42886         };
42887
42888
42889         function sets(params, n, o) {
42890             if (params.geometry && o[params.geometry]) {
42891                 params[n] = o[params.geometry];
42892             }
42893             return params;
42894         }
42895
42896
42897         function setFilter(params) {
42898             return sets(params, 'filter', tag_filters);
42899         }
42900
42901
42902         function setSort(params) {
42903             return sets(params, 'sortname', tag_sorts);
42904         }
42905
42906
42907         function setSortMembers(params) {
42908             return sets(params, 'sortname', tag_sort_members);
42909         }
42910
42911
42912         function clean(params) {
42913             return utilObjectOmit(params, ['geometry', 'debounce']);
42914         }
42915
42916
42917         function filterKeys(type) {
42918             var count_type = type ? 'count_' + type : 'count_all';
42919             return function(d) {
42920                 return parseFloat(d[count_type]) > 2500 || d.in_wiki;
42921             };
42922         }
42923
42924
42925         function filterMultikeys(prefix) {
42926             return function(d) {
42927                 // d.key begins with prefix, and d.key contains no additional ':'s
42928                 var re = new RegExp('^' + prefix + '(.*)$');
42929                 var matches = d.key.match(re) || [];
42930                 return (matches.length === 2 && matches[1].indexOf(':') === -1);
42931             };
42932         }
42933
42934
42935         function filterValues(allowUpperCase) {
42936             return function(d) {
42937                 if (d.value.match(/[;,]/) !== null) { return false; }  // exclude some punctuation
42938                 if (!allowUpperCase && d.value.match(/[A-Z*]/) !== null) { return false; }  // exclude uppercase letters
42939                 return parseFloat(d.fraction) > 0.0;
42940             };
42941         }
42942
42943
42944         function filterRoles(geometry) {
42945             return function(d) {
42946                 if (d.role === '') { return false; } // exclude empty role
42947                 if (d.role.match(/[A-Z*;,]/) !== null) { return false; }  // exclude uppercase letters and some punctuation
42948                 return parseFloat(d[tag_members_fractions[geometry]]) > 0.0;
42949             };
42950         }
42951
42952
42953         function valKey(d) {
42954             return {
42955                 value: d.key,
42956                 title: d.key
42957             };
42958         }
42959
42960
42961         function valKeyDescription(d) {
42962             var obj = {
42963                 value: d.value,
42964                 title: d.description || d.value
42965             };
42966             if (d.count) {
42967                 obj.count = d.count;
42968             }
42969             return obj;
42970         }
42971
42972
42973         function roleKey(d) {
42974             return {
42975                 value: d.role,
42976                 title: d.role
42977             };
42978         }
42979
42980
42981         // sort keys with ':' lower than keys without ':'
42982         function sortKeys(a, b) {
42983             return (a.key.indexOf(':') === -1 && b.key.indexOf(':') !== -1) ? -1
42984                 : (a.key.indexOf(':') !== -1 && b.key.indexOf(':') === -1) ? 1
42985                 : 0;
42986         }
42987
42988
42989         var debouncedRequest$1 = debounce(request$1, 300, { leading: false });
42990
42991         function request$1(url, params, exactMatch, callback, loaded) {
42992             if (_inflight$2[url]) { return; }
42993
42994             if (checkCache(url, params, exactMatch, callback)) { return; }
42995
42996             var controller = new AbortController();
42997             _inflight$2[url] = controller;
42998
42999             d3_json(url, { signal: controller.signal })
43000                 .then(function(result) {
43001                     delete _inflight$2[url];
43002                     if (loaded) { loaded(null, result); }
43003                 })
43004                 .catch(function(err) {
43005                     delete _inflight$2[url];
43006                     if (err.name === 'AbortError') { return; }
43007                     if (loaded) { loaded(err.message); }
43008                 });
43009         }
43010
43011
43012         function checkCache(url, params, exactMatch, callback) {
43013             var rp = params.rp || 25;
43014             var testQuery = params.query || '';
43015             var testUrl = url;
43016
43017             do {
43018                 var hit = _taginfoCache[testUrl];
43019
43020                 // exact match, or shorter match yielding fewer than max results (rp)
43021                 if (hit && (url === testUrl || hit.length < rp)) {
43022                     callback(null, hit);
43023                     return true;
43024                 }
43025
43026                 // don't try to shorten the query
43027                 if (exactMatch || !testQuery.length) { return false; }
43028
43029                 // do shorten the query to see if we already have a cached result
43030                 // that has returned fewer than max results (rp)
43031                 testQuery = testQuery.slice(0, -1);
43032                 testUrl = url.replace(/&query=(.*?)&/, '&query=' + testQuery + '&');
43033             } while (testQuery.length >= 0);
43034
43035             return false;
43036         }
43037
43038
43039         var serviceTaginfo = {
43040
43041             init: function() {
43042                 _inflight$2 = {};
43043                 _taginfoCache = {};
43044                 _popularKeys = {
43045                     // manually exclude some keys – #5377, #7485
43046                     postal_code: true,
43047                     full_name: true,
43048                     loc_name: true,
43049                     reg_name: true,
43050                     short_name: true,
43051                     sorting_name: true,
43052                     artist_name: true,
43053                     nat_name: true,
43054                     long_name: true,
43055                     'bridge:name': true
43056                 };
43057
43058                 // Fetch popular keys.  We'll exclude these from `values`
43059                 // lookups because they stress taginfo, and they aren't likely
43060                 // to yield meaningful autocomplete results.. see #3955
43061                 var params = {
43062                     rp: 100,
43063                     sortname: 'values_all',
43064                     sortorder: 'desc',
43065                     page: 1,
43066                     debounce: false,
43067                     lang: _mainLocalizer.languageCode()
43068                 };
43069                 this.keys(params, function(err, data) {
43070                     if (err) { return; }
43071                     data.forEach(function(d) {
43072                         if (d.value === 'opening_hours') { return; }  // exception
43073                         _popularKeys[d.value] = true;
43074                     });
43075                 });
43076             },
43077
43078
43079             reset: function() {
43080                 Object.values(_inflight$2).forEach(function(controller) { controller.abort(); });
43081                 _inflight$2 = {};
43082             },
43083
43084
43085             keys: function(params, callback) {
43086                 var doRequest = params.debounce ? debouncedRequest$1 : request$1;
43087                 params = clean(setSort(params));
43088                 params = Object.assign({
43089                     rp: 10,
43090                     sortname: 'count_all',
43091                     sortorder: 'desc',
43092                     page: 1,
43093                     lang: _mainLocalizer.languageCode()
43094                 }, params);
43095
43096                 var url = apibase$4 + 'keys/all?' + utilQsString(params);
43097                 doRequest(url, params, false, callback, function(err, d) {
43098                     if (err) {
43099                         callback(err);
43100                     } else {
43101                         var f = filterKeys(params.filter);
43102                         var result = d.data.filter(f).sort(sortKeys).map(valKey);
43103                         _taginfoCache[url] = result;
43104                         callback(null, result);
43105                     }
43106                 });
43107             },
43108
43109
43110             multikeys: function(params, callback) {
43111                 var doRequest = params.debounce ? debouncedRequest$1 : request$1;
43112                 params = clean(setSort(params));
43113                 params = Object.assign({
43114                     rp: 25,
43115                     sortname: 'count_all',
43116                     sortorder: 'desc',
43117                     page: 1,
43118                     lang: _mainLocalizer.languageCode()
43119                 }, params);
43120
43121                 var prefix = params.query;
43122                 var url = apibase$4 + 'keys/all?' + utilQsString(params);
43123                 doRequest(url, params, true, callback, function(err, d) {
43124                     if (err) {
43125                         callback(err);
43126                     } else {
43127                         var f = filterMultikeys(prefix);
43128                         var result = d.data.filter(f).map(valKey);
43129                         _taginfoCache[url] = result;
43130                         callback(null, result);
43131                     }
43132                 });
43133             },
43134
43135
43136             values: function(params, callback) {
43137                 // Exclude popular keys from values lookups.. see #3955
43138                 var key = params.key;
43139                 if (key && _popularKeys[key]) {
43140                     callback(null, []);
43141                     return;
43142                 }
43143
43144                 var doRequest = params.debounce ? debouncedRequest$1 : request$1;
43145                 params = clean(setSort(setFilter(params)));
43146                 params = Object.assign({
43147                     rp: 25,
43148                     sortname: 'count_all',
43149                     sortorder: 'desc',
43150                     page: 1,
43151                     lang: _mainLocalizer.languageCode()
43152                 }, params);
43153
43154                 var url = apibase$4 + 'key/values?' + utilQsString(params);
43155                 doRequest(url, params, false, callback, function(err, d) {
43156                     if (err) {
43157                         callback(err);
43158                     } else {
43159                         // In most cases we prefer taginfo value results with lowercase letters.
43160                         // A few OSM keys expect values to contain uppercase values (see #3377).
43161                         // This is not an exhaustive list (e.g. `name` also has uppercase values)
43162                         // but these are the fields where taginfo value lookup is most useful.
43163                         var re = /network|taxon|genus|species|brand|grape_variety|royal_cypher|listed_status|booth|rating|stars|:output|_hours|_times|_ref|manufacturer|country|target|brewery/;
43164                         var allowUpperCase = re.test(params.key);
43165                         var f = filterValues(allowUpperCase);
43166
43167                         var result = d.data.filter(f).map(valKeyDescription);
43168                         _taginfoCache[url] = result;
43169                         callback(null, result);
43170                     }
43171                 });
43172             },
43173
43174
43175             roles: function(params, callback) {
43176                 var doRequest = params.debounce ? debouncedRequest$1 : request$1;
43177                 var geometry = params.geometry;
43178                 params = clean(setSortMembers(params));
43179                 params = Object.assign({
43180                     rp: 25,
43181                     sortname: 'count_all_members',
43182                     sortorder: 'desc',
43183                     page: 1,
43184                     lang: _mainLocalizer.languageCode()
43185                 }, params);
43186
43187                 var url = apibase$4 + 'relation/roles?' + utilQsString(params);
43188                 doRequest(url, params, true, callback, function(err, d) {
43189                     if (err) {
43190                         callback(err);
43191                     } else {
43192                         var f = filterRoles(geometry);
43193                         var result = d.data.filter(f).map(roleKey);
43194                         _taginfoCache[url] = result;
43195                         callback(null, result);
43196                     }
43197                 });
43198             },
43199
43200
43201             docs: function(params, callback) {
43202                 var doRequest = params.debounce ? debouncedRequest$1 : request$1;
43203                 params = clean(setSort(params));
43204
43205                 var path = 'key/wiki_pages?';
43206                 if (params.value) {
43207                     path = 'tag/wiki_pages?';
43208                 } else if (params.rtype) {
43209                     path = 'relation/wiki_pages?';
43210                 }
43211
43212                 var url = apibase$4 + path + utilQsString(params);
43213                 doRequest(url, params, true, callback, function(err, d) {
43214                     if (err) {
43215                         callback(err);
43216                     } else {
43217                         _taginfoCache[url] = d.data;
43218                         callback(null, d.data);
43219                     }
43220                 });
43221             },
43222
43223
43224             apibase: function(_) {
43225                 if (!arguments.length) { return apibase$4; }
43226                 apibase$4 = _;
43227                 return this;
43228             }
43229
43230         };
43231
43232         var helpers$1 = createCommonjsModule(function (module, exports) {
43233         Object.defineProperty(exports, "__esModule", { value: true });
43234         /**
43235          * @module helpers
43236          */
43237         /**
43238          * Earth Radius used with the Harvesine formula and approximates using a spherical (non-ellipsoid) Earth.
43239          *
43240          * @memberof helpers
43241          * @type {number}
43242          */
43243         exports.earthRadius = 6371008.8;
43244         /**
43245          * Unit of measurement factors using a spherical (non-ellipsoid) earth radius.
43246          *
43247          * @memberof helpers
43248          * @type {Object}
43249          */
43250         exports.factors = {
43251             centimeters: exports.earthRadius * 100,
43252             centimetres: exports.earthRadius * 100,
43253             degrees: exports.earthRadius / 111325,
43254             feet: exports.earthRadius * 3.28084,
43255             inches: exports.earthRadius * 39.370,
43256             kilometers: exports.earthRadius / 1000,
43257             kilometres: exports.earthRadius / 1000,
43258             meters: exports.earthRadius,
43259             metres: exports.earthRadius,
43260             miles: exports.earthRadius / 1609.344,
43261             millimeters: exports.earthRadius * 1000,
43262             millimetres: exports.earthRadius * 1000,
43263             nauticalmiles: exports.earthRadius / 1852,
43264             radians: 1,
43265             yards: exports.earthRadius / 1.0936,
43266         };
43267         /**
43268          * Units of measurement factors based on 1 meter.
43269          *
43270          * @memberof helpers
43271          * @type {Object}
43272          */
43273         exports.unitsFactors = {
43274             centimeters: 100,
43275             centimetres: 100,
43276             degrees: 1 / 111325,
43277             feet: 3.28084,
43278             inches: 39.370,
43279             kilometers: 1 / 1000,
43280             kilometres: 1 / 1000,
43281             meters: 1,
43282             metres: 1,
43283             miles: 1 / 1609.344,
43284             millimeters: 1000,
43285             millimetres: 1000,
43286             nauticalmiles: 1 / 1852,
43287             radians: 1 / exports.earthRadius,
43288             yards: 1 / 1.0936,
43289         };
43290         /**
43291          * Area of measurement factors based on 1 square meter.
43292          *
43293          * @memberof helpers
43294          * @type {Object}
43295          */
43296         exports.areaFactors = {
43297             acres: 0.000247105,
43298             centimeters: 10000,
43299             centimetres: 10000,
43300             feet: 10.763910417,
43301             inches: 1550.003100006,
43302             kilometers: 0.000001,
43303             kilometres: 0.000001,
43304             meters: 1,
43305             metres: 1,
43306             miles: 3.86e-7,
43307             millimeters: 1000000,
43308             millimetres: 1000000,
43309             yards: 1.195990046,
43310         };
43311         /**
43312          * Wraps a GeoJSON {@link Geometry} in a GeoJSON {@link Feature}.
43313          *
43314          * @name feature
43315          * @param {Geometry} geometry input geometry
43316          * @param {Object} [properties={}] an Object of key-value pairs to add as properties
43317          * @param {Object} [options={}] Optional Parameters
43318          * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
43319          * @param {string|number} [options.id] Identifier associated with the Feature
43320          * @returns {Feature} a GeoJSON Feature
43321          * @example
43322          * var geometry = {
43323          *   "type": "Point",
43324          *   "coordinates": [110, 50]
43325          * };
43326          *
43327          * var feature = turf.feature(geometry);
43328          *
43329          * //=feature
43330          */
43331         function feature(geom, properties, options) {
43332             if (options === void 0) { options = {}; }
43333             var feat = { type: "Feature" };
43334             if (options.id === 0 || options.id) {
43335                 feat.id = options.id;
43336             }
43337             if (options.bbox) {
43338                 feat.bbox = options.bbox;
43339             }
43340             feat.properties = properties || {};
43341             feat.geometry = geom;
43342             return feat;
43343         }
43344         exports.feature = feature;
43345         /**
43346          * Creates a GeoJSON {@link Geometry} from a Geometry string type & coordinates.
43347          * For GeometryCollection type use `helpers.geometryCollection`
43348          *
43349          * @name geometry
43350          * @param {string} type Geometry Type
43351          * @param {Array<any>} coordinates Coordinates
43352          * @param {Object} [options={}] Optional Parameters
43353          * @returns {Geometry} a GeoJSON Geometry
43354          * @example
43355          * var type = "Point";
43356          * var coordinates = [110, 50];
43357          * var geometry = turf.geometry(type, coordinates);
43358          * // => geometry
43359          */
43360         function geometry(type, coordinates, options) {
43361             switch (type) {
43362                 case "Point": return point(coordinates).geometry;
43363                 case "LineString": return lineString(coordinates).geometry;
43364                 case "Polygon": return polygon(coordinates).geometry;
43365                 case "MultiPoint": return multiPoint(coordinates).geometry;
43366                 case "MultiLineString": return multiLineString(coordinates).geometry;
43367                 case "MultiPolygon": return multiPolygon(coordinates).geometry;
43368                 default: throw new Error(type + " is invalid");
43369             }
43370         }
43371         exports.geometry = geometry;
43372         /**
43373          * Creates a {@link Point} {@link Feature} from a Position.
43374          *
43375          * @name point
43376          * @param {Array<number>} coordinates longitude, latitude position (each in decimal degrees)
43377          * @param {Object} [properties={}] an Object of key-value pairs to add as properties
43378          * @param {Object} [options={}] Optional Parameters
43379          * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
43380          * @param {string|number} [options.id] Identifier associated with the Feature
43381          * @returns {Feature<Point>} a Point feature
43382          * @example
43383          * var point = turf.point([-75.343, 39.984]);
43384          *
43385          * //=point
43386          */
43387         function point(coordinates, properties, options) {
43388             if (options === void 0) { options = {}; }
43389             var geom = {
43390                 type: "Point",
43391                 coordinates: coordinates,
43392             };
43393             return feature(geom, properties, options);
43394         }
43395         exports.point = point;
43396         /**
43397          * Creates a {@link Point} {@link FeatureCollection} from an Array of Point coordinates.
43398          *
43399          * @name points
43400          * @param {Array<Array<number>>} coordinates an array of Points
43401          * @param {Object} [properties={}] Translate these properties to each Feature
43402          * @param {Object} [options={}] Optional Parameters
43403          * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north]
43404          * associated with the FeatureCollection
43405          * @param {string|number} [options.id] Identifier associated with the FeatureCollection
43406          * @returns {FeatureCollection<Point>} Point Feature
43407          * @example
43408          * var points = turf.points([
43409          *   [-75, 39],
43410          *   [-80, 45],
43411          *   [-78, 50]
43412          * ]);
43413          *
43414          * //=points
43415          */
43416         function points(coordinates, properties, options) {
43417             if (options === void 0) { options = {}; }
43418             return featureCollection(coordinates.map(function (coords) {
43419                 return point(coords, properties);
43420             }), options);
43421         }
43422         exports.points = points;
43423         /**
43424          * Creates a {@link Polygon} {@link Feature} from an Array of LinearRings.
43425          *
43426          * @name polygon
43427          * @param {Array<Array<Array<number>>>} coordinates an array of LinearRings
43428          * @param {Object} [properties={}] an Object of key-value pairs to add as properties
43429          * @param {Object} [options={}] Optional Parameters
43430          * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
43431          * @param {string|number} [options.id] Identifier associated with the Feature
43432          * @returns {Feature<Polygon>} Polygon Feature
43433          * @example
43434          * var polygon = turf.polygon([[[-5, 52], [-4, 56], [-2, 51], [-7, 54], [-5, 52]]], { name: 'poly1' });
43435          *
43436          * //=polygon
43437          */
43438         function polygon(coordinates, properties, options) {
43439             if (options === void 0) { options = {}; }
43440             for (var _i = 0, coordinates_1 = coordinates; _i < coordinates_1.length; _i++) {
43441                 var ring = coordinates_1[_i];
43442                 if (ring.length < 4) {
43443                     throw new Error("Each LinearRing of a Polygon must have 4 or more Positions.");
43444                 }
43445                 for (var j = 0; j < ring[ring.length - 1].length; j++) {
43446                     // Check if first point of Polygon contains two numbers
43447                     if (ring[ring.length - 1][j] !== ring[0][j]) {
43448                         throw new Error("First and last Position are not equivalent.");
43449                     }
43450                 }
43451             }
43452             var geom = {
43453                 type: "Polygon",
43454                 coordinates: coordinates,
43455             };
43456             return feature(geom, properties, options);
43457         }
43458         exports.polygon = polygon;
43459         /**
43460          * Creates a {@link Polygon} {@link FeatureCollection} from an Array of Polygon coordinates.
43461          *
43462          * @name polygons
43463          * @param {Array<Array<Array<Array<number>>>>} coordinates an array of Polygon coordinates
43464          * @param {Object} [properties={}] an Object of key-value pairs to add as properties
43465          * @param {Object} [options={}] Optional Parameters
43466          * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
43467          * @param {string|number} [options.id] Identifier associated with the FeatureCollection
43468          * @returns {FeatureCollection<Polygon>} Polygon FeatureCollection
43469          * @example
43470          * var polygons = turf.polygons([
43471          *   [[[-5, 52], [-4, 56], [-2, 51], [-7, 54], [-5, 52]]],
43472          *   [[[-15, 42], [-14, 46], [-12, 41], [-17, 44], [-15, 42]]],
43473          * ]);
43474          *
43475          * //=polygons
43476          */
43477         function polygons(coordinates, properties, options) {
43478             if (options === void 0) { options = {}; }
43479             return featureCollection(coordinates.map(function (coords) {
43480                 return polygon(coords, properties);
43481             }), options);
43482         }
43483         exports.polygons = polygons;
43484         /**
43485          * Creates a {@link LineString} {@link Feature} from an Array of Positions.
43486          *
43487          * @name lineString
43488          * @param {Array<Array<number>>} coordinates an array of Positions
43489          * @param {Object} [properties={}] an Object of key-value pairs to add as properties
43490          * @param {Object} [options={}] Optional Parameters
43491          * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
43492          * @param {string|number} [options.id] Identifier associated with the Feature
43493          * @returns {Feature<LineString>} LineString Feature
43494          * @example
43495          * var linestring1 = turf.lineString([[-24, 63], [-23, 60], [-25, 65], [-20, 69]], {name: 'line 1'});
43496          * var linestring2 = turf.lineString([[-14, 43], [-13, 40], [-15, 45], [-10, 49]], {name: 'line 2'});
43497          *
43498          * //=linestring1
43499          * //=linestring2
43500          */
43501         function lineString(coordinates, properties, options) {
43502             if (options === void 0) { options = {}; }
43503             if (coordinates.length < 2) {
43504                 throw new Error("coordinates must be an array of two or more positions");
43505             }
43506             var geom = {
43507                 type: "LineString",
43508                 coordinates: coordinates,
43509             };
43510             return feature(geom, properties, options);
43511         }
43512         exports.lineString = lineString;
43513         /**
43514          * Creates a {@link LineString} {@link FeatureCollection} from an Array of LineString coordinates.
43515          *
43516          * @name lineStrings
43517          * @param {Array<Array<Array<number>>>} coordinates an array of LinearRings
43518          * @param {Object} [properties={}] an Object of key-value pairs to add as properties
43519          * @param {Object} [options={}] Optional Parameters
43520          * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north]
43521          * associated with the FeatureCollection
43522          * @param {string|number} [options.id] Identifier associated with the FeatureCollection
43523          * @returns {FeatureCollection<LineString>} LineString FeatureCollection
43524          * @example
43525          * var linestrings = turf.lineStrings([
43526          *   [[-24, 63], [-23, 60], [-25, 65], [-20, 69]],
43527          *   [[-14, 43], [-13, 40], [-15, 45], [-10, 49]]
43528          * ]);
43529          *
43530          * //=linestrings
43531          */
43532         function lineStrings(coordinates, properties, options) {
43533             if (options === void 0) { options = {}; }
43534             return featureCollection(coordinates.map(function (coords) {
43535                 return lineString(coords, properties);
43536             }), options);
43537         }
43538         exports.lineStrings = lineStrings;
43539         /**
43540          * Takes one or more {@link Feature|Features} and creates a {@link FeatureCollection}.
43541          *
43542          * @name featureCollection
43543          * @param {Feature[]} features input features
43544          * @param {Object} [options={}] Optional Parameters
43545          * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
43546          * @param {string|number} [options.id] Identifier associated with the Feature
43547          * @returns {FeatureCollection} FeatureCollection of Features
43548          * @example
43549          * var locationA = turf.point([-75.343, 39.984], {name: 'Location A'});
43550          * var locationB = turf.point([-75.833, 39.284], {name: 'Location B'});
43551          * var locationC = turf.point([-75.534, 39.123], {name: 'Location C'});
43552          *
43553          * var collection = turf.featureCollection([
43554          *   locationA,
43555          *   locationB,
43556          *   locationC
43557          * ]);
43558          *
43559          * //=collection
43560          */
43561         function featureCollection(features, options) {
43562             if (options === void 0) { options = {}; }
43563             var fc = { type: "FeatureCollection" };
43564             if (options.id) {
43565                 fc.id = options.id;
43566             }
43567             if (options.bbox) {
43568                 fc.bbox = options.bbox;
43569             }
43570             fc.features = features;
43571             return fc;
43572         }
43573         exports.featureCollection = featureCollection;
43574         /**
43575          * Creates a {@link Feature<MultiLineString>} based on a
43576          * coordinate array. Properties can be added optionally.
43577          *
43578          * @name multiLineString
43579          * @param {Array<Array<Array<number>>>} coordinates an array of LineStrings
43580          * @param {Object} [properties={}] an Object of key-value pairs to add as properties
43581          * @param {Object} [options={}] Optional Parameters
43582          * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
43583          * @param {string|number} [options.id] Identifier associated with the Feature
43584          * @returns {Feature<MultiLineString>} a MultiLineString feature
43585          * @throws {Error} if no coordinates are passed
43586          * @example
43587          * var multiLine = turf.multiLineString([[[0,0],[10,10]]]);
43588          *
43589          * //=multiLine
43590          */
43591         function multiLineString(coordinates, properties, options) {
43592             if (options === void 0) { options = {}; }
43593             var geom = {
43594                 type: "MultiLineString",
43595                 coordinates: coordinates,
43596             };
43597             return feature(geom, properties, options);
43598         }
43599         exports.multiLineString = multiLineString;
43600         /**
43601          * Creates a {@link Feature<MultiPoint>} based on a
43602          * coordinate array. Properties can be added optionally.
43603          *
43604          * @name multiPoint
43605          * @param {Array<Array<number>>} coordinates an array of Positions
43606          * @param {Object} [properties={}] an Object of key-value pairs to add as properties
43607          * @param {Object} [options={}] Optional Parameters
43608          * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
43609          * @param {string|number} [options.id] Identifier associated with the Feature
43610          * @returns {Feature<MultiPoint>} a MultiPoint feature
43611          * @throws {Error} if no coordinates are passed
43612          * @example
43613          * var multiPt = turf.multiPoint([[0,0],[10,10]]);
43614          *
43615          * //=multiPt
43616          */
43617         function multiPoint(coordinates, properties, options) {
43618             if (options === void 0) { options = {}; }
43619             var geom = {
43620                 type: "MultiPoint",
43621                 coordinates: coordinates,
43622             };
43623             return feature(geom, properties, options);
43624         }
43625         exports.multiPoint = multiPoint;
43626         /**
43627          * Creates a {@link Feature<MultiPolygon>} based on a
43628          * coordinate array. Properties can be added optionally.
43629          *
43630          * @name multiPolygon
43631          * @param {Array<Array<Array<Array<number>>>>} coordinates an array of Polygons
43632          * @param {Object} [properties={}] an Object of key-value pairs to add as properties
43633          * @param {Object} [options={}] Optional Parameters
43634          * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
43635          * @param {string|number} [options.id] Identifier associated with the Feature
43636          * @returns {Feature<MultiPolygon>} a multipolygon feature
43637          * @throws {Error} if no coordinates are passed
43638          * @example
43639          * var multiPoly = turf.multiPolygon([[[[0,0],[0,10],[10,10],[10,0],[0,0]]]]);
43640          *
43641          * //=multiPoly
43642          *
43643          */
43644         function multiPolygon(coordinates, properties, options) {
43645             if (options === void 0) { options = {}; }
43646             var geom = {
43647                 type: "MultiPolygon",
43648                 coordinates: coordinates,
43649             };
43650             return feature(geom, properties, options);
43651         }
43652         exports.multiPolygon = multiPolygon;
43653         /**
43654          * Creates a {@link Feature<GeometryCollection>} based on a
43655          * coordinate array. Properties can be added optionally.
43656          *
43657          * @name geometryCollection
43658          * @param {Array<Geometry>} geometries an array of GeoJSON Geometries
43659          * @param {Object} [properties={}] an Object of key-value pairs to add as properties
43660          * @param {Object} [options={}] Optional Parameters
43661          * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
43662          * @param {string|number} [options.id] Identifier associated with the Feature
43663          * @returns {Feature<GeometryCollection>} a GeoJSON GeometryCollection Feature
43664          * @example
43665          * var pt = turf.geometry("Point", [100, 0]);
43666          * var line = turf.geometry("LineString", [[101, 0], [102, 1]]);
43667          * var collection = turf.geometryCollection([pt, line]);
43668          *
43669          * // => collection
43670          */
43671         function geometryCollection(geometries, properties, options) {
43672             if (options === void 0) { options = {}; }
43673             var geom = {
43674                 type: "GeometryCollection",
43675                 geometries: geometries,
43676             };
43677             return feature(geom, properties, options);
43678         }
43679         exports.geometryCollection = geometryCollection;
43680         /**
43681          * Round number to precision
43682          *
43683          * @param {number} num Number
43684          * @param {number} [precision=0] Precision
43685          * @returns {number} rounded number
43686          * @example
43687          * turf.round(120.4321)
43688          * //=120
43689          *
43690          * turf.round(120.4321, 2)
43691          * //=120.43
43692          */
43693         function round(num, precision) {
43694             if (precision === void 0) { precision = 0; }
43695             if (precision && !(precision >= 0)) {
43696                 throw new Error("precision must be a positive number");
43697             }
43698             var multiplier = Math.pow(10, precision || 0);
43699             return Math.round(num * multiplier) / multiplier;
43700         }
43701         exports.round = round;
43702         /**
43703          * Convert a distance measurement (assuming a spherical Earth) from radians to a more friendly unit.
43704          * Valid units: miles, nauticalmiles, inches, yards, meters, metres, kilometers, centimeters, feet
43705          *
43706          * @name radiansToLength
43707          * @param {number} radians in radians across the sphere
43708          * @param {string} [units="kilometers"] can be degrees, radians, miles, or kilometers inches, yards, metres,
43709          * meters, kilometres, kilometers.
43710          * @returns {number} distance
43711          */
43712         function radiansToLength(radians, units) {
43713             if (units === void 0) { units = "kilometers"; }
43714             var factor = exports.factors[units];
43715             if (!factor) {
43716                 throw new Error(units + " units is invalid");
43717             }
43718             return radians * factor;
43719         }
43720         exports.radiansToLength = radiansToLength;
43721         /**
43722          * Convert a distance measurement (assuming a spherical Earth) from a real-world unit into radians
43723          * Valid units: miles, nauticalmiles, inches, yards, meters, metres, kilometers, centimeters, feet
43724          *
43725          * @name lengthToRadians
43726          * @param {number} distance in real units
43727          * @param {string} [units="kilometers"] can be degrees, radians, miles, or kilometers inches, yards, metres,
43728          * meters, kilometres, kilometers.
43729          * @returns {number} radians
43730          */
43731         function lengthToRadians(distance, units) {
43732             if (units === void 0) { units = "kilometers"; }
43733             var factor = exports.factors[units];
43734             if (!factor) {
43735                 throw new Error(units + " units is invalid");
43736             }
43737             return distance / factor;
43738         }
43739         exports.lengthToRadians = lengthToRadians;
43740         /**
43741          * Convert a distance measurement (assuming a spherical Earth) from a real-world unit into degrees
43742          * Valid units: miles, nauticalmiles, inches, yards, meters, metres, centimeters, kilometres, feet
43743          *
43744          * @name lengthToDegrees
43745          * @param {number} distance in real units
43746          * @param {string} [units="kilometers"] can be degrees, radians, miles, or kilometers inches, yards, metres,
43747          * meters, kilometres, kilometers.
43748          * @returns {number} degrees
43749          */
43750         function lengthToDegrees(distance, units) {
43751             return radiansToDegrees(lengthToRadians(distance, units));
43752         }
43753         exports.lengthToDegrees = lengthToDegrees;
43754         /**
43755          * Converts any bearing angle from the north line direction (positive clockwise)
43756          * and returns an angle between 0-360 degrees (positive clockwise), 0 being the north line
43757          *
43758          * @name bearingToAzimuth
43759          * @param {number} bearing angle, between -180 and +180 degrees
43760          * @returns {number} angle between 0 and 360 degrees
43761          */
43762         function bearingToAzimuth(bearing) {
43763             var angle = bearing % 360;
43764             if (angle < 0) {
43765                 angle += 360;
43766             }
43767             return angle;
43768         }
43769         exports.bearingToAzimuth = bearingToAzimuth;
43770         /**
43771          * Converts an angle in radians to degrees
43772          *
43773          * @name radiansToDegrees
43774          * @param {number} radians angle in radians
43775          * @returns {number} degrees between 0 and 360 degrees
43776          */
43777         function radiansToDegrees(radians) {
43778             var degrees = radians % (2 * Math.PI);
43779             return degrees * 180 / Math.PI;
43780         }
43781         exports.radiansToDegrees = radiansToDegrees;
43782         /**
43783          * Converts an angle in degrees to radians
43784          *
43785          * @name degreesToRadians
43786          * @param {number} degrees angle between 0 and 360 degrees
43787          * @returns {number} angle in radians
43788          */
43789         function degreesToRadians(degrees) {
43790             var radians = degrees % 360;
43791             return radians * Math.PI / 180;
43792         }
43793         exports.degreesToRadians = degreesToRadians;
43794         /**
43795          * Converts a length to the requested unit.
43796          * Valid units: miles, nauticalmiles, inches, yards, meters, metres, kilometers, centimeters, feet
43797          *
43798          * @param {number} length to be converted
43799          * @param {Units} [originalUnit="kilometers"] of the length
43800          * @param {Units} [finalUnit="kilometers"] returned unit
43801          * @returns {number} the converted length
43802          */
43803         function convertLength(length, originalUnit, finalUnit) {
43804             if (originalUnit === void 0) { originalUnit = "kilometers"; }
43805             if (finalUnit === void 0) { finalUnit = "kilometers"; }
43806             if (!(length >= 0)) {
43807                 throw new Error("length must be a positive number");
43808             }
43809             return radiansToLength(lengthToRadians(length, originalUnit), finalUnit);
43810         }
43811         exports.convertLength = convertLength;
43812         /**
43813          * Converts a area to the requested unit.
43814          * Valid units: kilometers, kilometres, meters, metres, centimetres, millimeters, acres, miles, yards, feet, inches
43815          * @param {number} area to be converted
43816          * @param {Units} [originalUnit="meters"] of the distance
43817          * @param {Units} [finalUnit="kilometers"] returned unit
43818          * @returns {number} the converted distance
43819          */
43820         function convertArea(area, originalUnit, finalUnit) {
43821             if (originalUnit === void 0) { originalUnit = "meters"; }
43822             if (finalUnit === void 0) { finalUnit = "kilometers"; }
43823             if (!(area >= 0)) {
43824                 throw new Error("area must be a positive number");
43825             }
43826             var startFactor = exports.areaFactors[originalUnit];
43827             if (!startFactor) {
43828                 throw new Error("invalid original units");
43829             }
43830             var finalFactor = exports.areaFactors[finalUnit];
43831             if (!finalFactor) {
43832                 throw new Error("invalid final units");
43833             }
43834             return (area / startFactor) * finalFactor;
43835         }
43836         exports.convertArea = convertArea;
43837         /**
43838          * isNumber
43839          *
43840          * @param {*} num Number to validate
43841          * @returns {boolean} true/false
43842          * @example
43843          * turf.isNumber(123)
43844          * //=true
43845          * turf.isNumber('foo')
43846          * //=false
43847          */
43848         function isNumber(num) {
43849             return !isNaN(num) && num !== null && !Array.isArray(num) && !/^\s*$/.test(num);
43850         }
43851         exports.isNumber = isNumber;
43852         /**
43853          * isObject
43854          *
43855          * @param {*} input variable to validate
43856          * @returns {boolean} true/false
43857          * @example
43858          * turf.isObject({elevation: 10})
43859          * //=true
43860          * turf.isObject('foo')
43861          * //=false
43862          */
43863         function isObject(input) {
43864             return (!!input) && (input.constructor === Object);
43865         }
43866         exports.isObject = isObject;
43867         /**
43868          * Validate BBox
43869          *
43870          * @private
43871          * @param {Array<number>} bbox BBox to validate
43872          * @returns {void}
43873          * @throws Error if BBox is not valid
43874          * @example
43875          * validateBBox([-180, -40, 110, 50])
43876          * //=OK
43877          * validateBBox([-180, -40])
43878          * //=Error
43879          * validateBBox('Foo')
43880          * //=Error
43881          * validateBBox(5)
43882          * //=Error
43883          * validateBBox(null)
43884          * //=Error
43885          * validateBBox(undefined)
43886          * //=Error
43887          */
43888         function validateBBox(bbox) {
43889             if (!bbox) {
43890                 throw new Error("bbox is required");
43891             }
43892             if (!Array.isArray(bbox)) {
43893                 throw new Error("bbox must be an Array");
43894             }
43895             if (bbox.length !== 4 && bbox.length !== 6) {
43896                 throw new Error("bbox must be an Array of 4 or 6 numbers");
43897             }
43898             bbox.forEach(function (num) {
43899                 if (!isNumber(num)) {
43900                     throw new Error("bbox must only contain numbers");
43901                 }
43902             });
43903         }
43904         exports.validateBBox = validateBBox;
43905         /**
43906          * Validate Id
43907          *
43908          * @private
43909          * @param {string|number} id Id to validate
43910          * @returns {void}
43911          * @throws Error if Id is not valid
43912          * @example
43913          * validateId([-180, -40, 110, 50])
43914          * //=Error
43915          * validateId([-180, -40])
43916          * //=Error
43917          * validateId('Foo')
43918          * //=OK
43919          * validateId(5)
43920          * //=OK
43921          * validateId(null)
43922          * //=Error
43923          * validateId(undefined)
43924          * //=Error
43925          */
43926         function validateId(id) {
43927             if (!id) {
43928                 throw new Error("id is required");
43929             }
43930             if (["string", "number"].indexOf(typeof id) === -1) {
43931                 throw new Error("id must be a number or a string");
43932             }
43933         }
43934         exports.validateId = validateId;
43935         // Deprecated methods
43936         function radians2degrees() {
43937             throw new Error("method has been renamed to `radiansToDegrees`");
43938         }
43939         exports.radians2degrees = radians2degrees;
43940         function degrees2radians() {
43941             throw new Error("method has been renamed to `degreesToRadians`");
43942         }
43943         exports.degrees2radians = degrees2radians;
43944         function distanceToDegrees() {
43945             throw new Error("method has been renamed to `lengthToDegrees`");
43946         }
43947         exports.distanceToDegrees = distanceToDegrees;
43948         function distanceToRadians() {
43949             throw new Error("method has been renamed to `lengthToRadians`");
43950         }
43951         exports.distanceToRadians = distanceToRadians;
43952         function radiansToDistance() {
43953             throw new Error("method has been renamed to `radiansToLength`");
43954         }
43955         exports.radiansToDistance = radiansToDistance;
43956         function bearingToAngle() {
43957             throw new Error("method has been renamed to `bearingToAzimuth`");
43958         }
43959         exports.bearingToAngle = bearingToAngle;
43960         function convertDistance() {
43961             throw new Error("method has been renamed to `convertLength`");
43962         }
43963         exports.convertDistance = convertDistance;
43964         });
43965
43966         var invariant = createCommonjsModule(function (module, exports) {
43967         Object.defineProperty(exports, "__esModule", { value: true });
43968
43969         /**
43970          * Unwrap a coordinate from a Point Feature, Geometry or a single coordinate.
43971          *
43972          * @name getCoord
43973          * @param {Array<number>|Geometry<Point>|Feature<Point>} coord GeoJSON Point or an Array of numbers
43974          * @returns {Array<number>} coordinates
43975          * @example
43976          * var pt = turf.point([10, 10]);
43977          *
43978          * var coord = turf.getCoord(pt);
43979          * //= [10, 10]
43980          */
43981         function getCoord(coord) {
43982             if (!coord) {
43983                 throw new Error("coord is required");
43984             }
43985             if (!Array.isArray(coord)) {
43986                 if (coord.type === "Feature" && coord.geometry !== null && coord.geometry.type === "Point") {
43987                     return coord.geometry.coordinates;
43988                 }
43989                 if (coord.type === "Point") {
43990                     return coord.coordinates;
43991                 }
43992             }
43993             if (Array.isArray(coord) && coord.length >= 2 && !Array.isArray(coord[0]) && !Array.isArray(coord[1])) {
43994                 return coord;
43995             }
43996             throw new Error("coord must be GeoJSON Point or an Array of numbers");
43997         }
43998         exports.getCoord = getCoord;
43999         /**
44000          * Unwrap coordinates from a Feature, Geometry Object or an Array
44001          *
44002          * @name getCoords
44003          * @param {Array<any>|Geometry|Feature} coords Feature, Geometry Object or an Array
44004          * @returns {Array<any>} coordinates
44005          * @example
44006          * var poly = turf.polygon([[[119.32, -8.7], [119.55, -8.69], [119.51, -8.54], [119.32, -8.7]]]);
44007          *
44008          * var coords = turf.getCoords(poly);
44009          * //= [[[119.32, -8.7], [119.55, -8.69], [119.51, -8.54], [119.32, -8.7]]]
44010          */
44011         function getCoords(coords) {
44012             if (Array.isArray(coords)) {
44013                 return coords;
44014             }
44015             // Feature
44016             if (coords.type === "Feature") {
44017                 if (coords.geometry !== null) {
44018                     return coords.geometry.coordinates;
44019                 }
44020             }
44021             else {
44022                 // Geometry
44023                 if (coords.coordinates) {
44024                     return coords.coordinates;
44025                 }
44026             }
44027             throw new Error("coords must be GeoJSON Feature, Geometry Object or an Array");
44028         }
44029         exports.getCoords = getCoords;
44030         /**
44031          * Checks if coordinates contains a number
44032          *
44033          * @name containsNumber
44034          * @param {Array<any>} coordinates GeoJSON Coordinates
44035          * @returns {boolean} true if Array contains a number
44036          */
44037         function containsNumber(coordinates) {
44038             if (coordinates.length > 1 && helpers$1.isNumber(coordinates[0]) && helpers$1.isNumber(coordinates[1])) {
44039                 return true;
44040             }
44041             if (Array.isArray(coordinates[0]) && coordinates[0].length) {
44042                 return containsNumber(coordinates[0]);
44043             }
44044             throw new Error("coordinates must only contain numbers");
44045         }
44046         exports.containsNumber = containsNumber;
44047         /**
44048          * Enforce expectations about types of GeoJSON objects for Turf.
44049          *
44050          * @name geojsonType
44051          * @param {GeoJSON} value any GeoJSON object
44052          * @param {string} type expected GeoJSON type
44053          * @param {string} name name of calling function
44054          * @throws {Error} if value is not the expected type.
44055          */
44056         function geojsonType(value, type, name) {
44057             if (!type || !name) {
44058                 throw new Error("type and name required");
44059             }
44060             if (!value || value.type !== type) {
44061                 throw new Error("Invalid input to " + name + ": must be a " + type + ", given " + value.type);
44062             }
44063         }
44064         exports.geojsonType = geojsonType;
44065         /**
44066          * Enforce expectations about types of {@link Feature} inputs for Turf.
44067          * Internally this uses {@link geojsonType} to judge geometry types.
44068          *
44069          * @name featureOf
44070          * @param {Feature} feature a feature with an expected geometry type
44071          * @param {string} type expected GeoJSON type
44072          * @param {string} name name of calling function
44073          * @throws {Error} error if value is not the expected type.
44074          */
44075         function featureOf(feature, type, name) {
44076             if (!feature) {
44077                 throw new Error("No feature passed");
44078             }
44079             if (!name) {
44080                 throw new Error(".featureOf() requires a name");
44081             }
44082             if (!feature || feature.type !== "Feature" || !feature.geometry) {
44083                 throw new Error("Invalid input to " + name + ", Feature with geometry required");
44084             }
44085             if (!feature.geometry || feature.geometry.type !== type) {
44086                 throw new Error("Invalid input to " + name + ": must be a " + type + ", given " + feature.geometry.type);
44087             }
44088         }
44089         exports.featureOf = featureOf;
44090         /**
44091          * Enforce expectations about types of {@link FeatureCollection} inputs for Turf.
44092          * Internally this uses {@link geojsonType} to judge geometry types.
44093          *
44094          * @name collectionOf
44095          * @param {FeatureCollection} featureCollection a FeatureCollection for which features will be judged
44096          * @param {string} type expected GeoJSON type
44097          * @param {string} name name of calling function
44098          * @throws {Error} if value is not the expected type.
44099          */
44100         function collectionOf(featureCollection, type, name) {
44101             if (!featureCollection) {
44102                 throw new Error("No featureCollection passed");
44103             }
44104             if (!name) {
44105                 throw new Error(".collectionOf() requires a name");
44106             }
44107             if (!featureCollection || featureCollection.type !== "FeatureCollection") {
44108                 throw new Error("Invalid input to " + name + ", FeatureCollection required");
44109             }
44110             for (var _i = 0, _a = featureCollection.features; _i < _a.length; _i++) {
44111                 var feature = _a[_i];
44112                 if (!feature || feature.type !== "Feature" || !feature.geometry) {
44113                     throw new Error("Invalid input to " + name + ", Feature with geometry required");
44114                 }
44115                 if (!feature.geometry || feature.geometry.type !== type) {
44116                     throw new Error("Invalid input to " + name + ": must be a " + type + ", given " + feature.geometry.type);
44117                 }
44118             }
44119         }
44120         exports.collectionOf = collectionOf;
44121         /**
44122          * Get Geometry from Feature or Geometry Object
44123          *
44124          * @param {Feature|Geometry} geojson GeoJSON Feature or Geometry Object
44125          * @returns {Geometry|null} GeoJSON Geometry Object
44126          * @throws {Error} if geojson is not a Feature or Geometry Object
44127          * @example
44128          * var point = {
44129          *   "type": "Feature",
44130          *   "properties": {},
44131          *   "geometry": {
44132          *     "type": "Point",
44133          *     "coordinates": [110, 40]
44134          *   }
44135          * }
44136          * var geom = turf.getGeom(point)
44137          * //={"type": "Point", "coordinates": [110, 40]}
44138          */
44139         function getGeom(geojson) {
44140             if (geojson.type === "Feature") {
44141                 return geojson.geometry;
44142             }
44143             return geojson;
44144         }
44145         exports.getGeom = getGeom;
44146         /**
44147          * Get GeoJSON object's type, Geometry type is prioritize.
44148          *
44149          * @param {GeoJSON} geojson GeoJSON object
44150          * @param {string} [name="geojson"] name of the variable to display in error message
44151          * @returns {string} GeoJSON type
44152          * @example
44153          * var point = {
44154          *   "type": "Feature",
44155          *   "properties": {},
44156          *   "geometry": {
44157          *     "type": "Point",
44158          *     "coordinates": [110, 40]
44159          *   }
44160          * }
44161          * var geom = turf.getType(point)
44162          * //="Point"
44163          */
44164         function getType(geojson, name) {
44165             if (geojson.type === "FeatureCollection") {
44166                 return "FeatureCollection";
44167             }
44168             if (geojson.type === "GeometryCollection") {
44169                 return "GeometryCollection";
44170             }
44171             if (geojson.type === "Feature" && geojson.geometry !== null) {
44172                 return geojson.geometry.type;
44173             }
44174             return geojson.type;
44175         }
44176         exports.getType = getType;
44177         });
44178
44179         var lineclip_1 = lineclip;
44180         var _default = lineclip;
44181
44182         lineclip.polyline = lineclip;
44183         lineclip.polygon = polygonclip;
44184
44185
44186         // Cohen-Sutherland line clippign algorithm, adapted to efficiently
44187         // handle polylines rather than just segments
44188
44189         function lineclip(points, bbox, result) {
44190
44191             var len = points.length,
44192                 codeA = bitCode(points[0], bbox),
44193                 part = [],
44194                 i, a, b, codeB, lastCode;
44195
44196             if (!result) { result = []; }
44197
44198             for (i = 1; i < len; i++) {
44199                 a = points[i - 1];
44200                 b = points[i];
44201                 codeB = lastCode = bitCode(b, bbox);
44202
44203                 while (true) {
44204
44205                     if (!(codeA | codeB)) { // accept
44206                         part.push(a);
44207
44208                         if (codeB !== lastCode) { // segment went outside
44209                             part.push(b);
44210
44211                             if (i < len - 1) { // start a new line
44212                                 result.push(part);
44213                                 part = [];
44214                             }
44215                         } else if (i === len - 1) {
44216                             part.push(b);
44217                         }
44218                         break;
44219
44220                     } else if (codeA & codeB) { // trivial reject
44221                         break;
44222
44223                     } else if (codeA) { // a outside, intersect with clip edge
44224                         a = intersect(a, b, codeA, bbox);
44225                         codeA = bitCode(a, bbox);
44226
44227                     } else { // b outside
44228                         b = intersect(a, b, codeB, bbox);
44229                         codeB = bitCode(b, bbox);
44230                     }
44231                 }
44232
44233                 codeA = lastCode;
44234             }
44235
44236             if (part.length) { result.push(part); }
44237
44238             return result;
44239         }
44240
44241         // Sutherland-Hodgeman polygon clipping algorithm
44242
44243         function polygonclip(points, bbox) {
44244
44245             var result, edge, prev, prevInside, i, p, inside;
44246
44247             // clip against each side of the clip rectangle
44248             for (edge = 1; edge <= 8; edge *= 2) {
44249                 result = [];
44250                 prev = points[points.length - 1];
44251                 prevInside = !(bitCode(prev, bbox) & edge);
44252
44253                 for (i = 0; i < points.length; i++) {
44254                     p = points[i];
44255                     inside = !(bitCode(p, bbox) & edge);
44256
44257                     // if segment goes through the clip window, add an intersection
44258                     if (inside !== prevInside) { result.push(intersect(prev, p, edge, bbox)); }
44259
44260                     if (inside) { result.push(p); } // add a point if it's inside
44261
44262                     prev = p;
44263                     prevInside = inside;
44264                 }
44265
44266                 points = result;
44267
44268                 if (!points.length) { break; }
44269             }
44270
44271             return result;
44272         }
44273
44274         // intersect a segment against one of the 4 lines that make up the bbox
44275
44276         function intersect(a, b, edge, bbox) {
44277             return edge & 8 ? [a[0] + (b[0] - a[0]) * (bbox[3] - a[1]) / (b[1] - a[1]), bbox[3]] : // top
44278                    edge & 4 ? [a[0] + (b[0] - a[0]) * (bbox[1] - a[1]) / (b[1] - a[1]), bbox[1]] : // bottom
44279                    edge & 2 ? [bbox[2], a[1] + (b[1] - a[1]) * (bbox[2] - a[0]) / (b[0] - a[0])] : // right
44280                    edge & 1 ? [bbox[0], a[1] + (b[1] - a[1]) * (bbox[0] - a[0]) / (b[0] - a[0])] : // left
44281                    null;
44282         }
44283
44284         // bit code reflects the point position relative to the bbox:
44285
44286         //         left  mid  right
44287         //    top  1001  1000  1010
44288         //    mid  0001  0000  0010
44289         // bottom  0101  0100  0110
44290
44291         function bitCode(p, bbox) {
44292             var code = 0;
44293
44294             if (p[0] < bbox[0]) { code |= 1; } // left
44295             else if (p[0] > bbox[2]) { code |= 2; } // right
44296
44297             if (p[1] < bbox[1]) { code |= 4; } // bottom
44298             else if (p[1] > bbox[3]) { code |= 8; } // top
44299
44300             return code;
44301         }
44302         lineclip_1.default = _default;
44303
44304         var bboxClip_1 = createCommonjsModule(function (module, exports) {
44305         var __importStar = (commonjsGlobal && commonjsGlobal.__importStar) || function (mod) {
44306             if (mod && mod.__esModule) { return mod; }
44307             var result = {};
44308             if (mod != null) { for (var k in mod) { if (Object.hasOwnProperty.call(mod, k)) { result[k] = mod[k]; } } }
44309             result["default"] = mod;
44310             return result;
44311         };
44312         Object.defineProperty(exports, "__esModule", { value: true });
44313
44314
44315         var lineclip = __importStar(lineclip_1);
44316         /**
44317          * Takes a {@link Feature} and a bbox and clips the feature to the bbox using
44318          * [lineclip](https://github.com/mapbox/lineclip).
44319          * May result in degenerate edges when clipping Polygons.
44320          *
44321          * @name bboxClip
44322          * @param {Feature<LineString|MultiLineString|Polygon|MultiPolygon>} feature feature to clip to the bbox
44323          * @param {BBox} bbox extent in [minX, minY, maxX, maxY] order
44324          * @returns {Feature<LineString|MultiLineString|Polygon|MultiPolygon>} clipped Feature
44325          * @example
44326          * var bbox = [0, 0, 10, 10];
44327          * var poly = turf.polygon([[[2, 2], [8, 4], [12, 8], [3, 7], [2, 2]]]);
44328          *
44329          * var clipped = turf.bboxClip(poly, bbox);
44330          *
44331          * //addToMap
44332          * var addToMap = [bbox, poly, clipped]
44333          */
44334         function bboxClip(feature, bbox) {
44335             var geom = invariant.getGeom(feature);
44336             var type = geom.type;
44337             var properties = feature.type === "Feature" ? feature.properties : {};
44338             var coords = geom.coordinates;
44339             switch (type) {
44340                 case "LineString":
44341                 case "MultiLineString":
44342                     var lines_1 = [];
44343                     if (type === "LineString") {
44344                         coords = [coords];
44345                     }
44346                     coords.forEach(function (line) {
44347                         lineclip.polyline(line, bbox, lines_1);
44348                     });
44349                     if (lines_1.length === 1) {
44350                         return helpers$1.lineString(lines_1[0], properties);
44351                     }
44352                     return helpers$1.multiLineString(lines_1, properties);
44353                 case "Polygon":
44354                     return helpers$1.polygon(clipPolygon(coords, bbox), properties);
44355                 case "MultiPolygon":
44356                     return helpers$1.multiPolygon(coords.map(function (poly) {
44357                         return clipPolygon(poly, bbox);
44358                     }), properties);
44359                 default:
44360                     throw new Error("geometry " + type + " not supported");
44361             }
44362         }
44363         exports.default = bboxClip;
44364         function clipPolygon(rings, bbox) {
44365             var outRings = [];
44366             for (var _i = 0, rings_1 = rings; _i < rings_1.length; _i++) {
44367                 var ring = rings_1[_i];
44368                 var clipped = lineclip.polygon(ring, bbox);
44369                 if (clipped.length > 0) {
44370                     if (clipped[0][0] !== clipped[clipped.length - 1][0] || clipped[0][1] !== clipped[clipped.length - 1][1]) {
44371                         clipped.push(clipped[0]);
44372                     }
44373                     if (clipped.length >= 4) {
44374                         outRings.push(clipped);
44375                     }
44376                 }
44377             }
44378             return outRings;
44379         }
44380         });
44381
44382         var turf_bboxClip = /*@__PURE__*/getDefaultExportFromCjs(bboxClip_1);
44383
44384         var fastJsonStableStringify = function (data, opts) {
44385             if (!opts) { opts = {}; }
44386             if (typeof opts === 'function') { opts = { cmp: opts }; }
44387             var cycles = (typeof opts.cycles === 'boolean') ? opts.cycles : false;
44388
44389             var cmp = opts.cmp && (function (f) {
44390                 return function (node) {
44391                     return function (a, b) {
44392                         var aobj = { key: a, value: node[a] };
44393                         var bobj = { key: b, value: node[b] };
44394                         return f(aobj, bobj);
44395                     };
44396                 };
44397             })(opts.cmp);
44398
44399             var seen = [];
44400             return (function stringify (node) {
44401                 if (node && node.toJSON && typeof node.toJSON === 'function') {
44402                     node = node.toJSON();
44403                 }
44404
44405                 if (node === undefined) { return; }
44406                 if (typeof node == 'number') { return isFinite(node) ? '' + node : 'null'; }
44407                 if (typeof node !== 'object') { return JSON.stringify(node); }
44408
44409                 var i, out;
44410                 if (Array.isArray(node)) {
44411                     out = '[';
44412                     for (i = 0; i < node.length; i++) {
44413                         if (i) { out += ','; }
44414                         out += stringify(node[i]) || 'null';
44415                     }
44416                     return out + ']';
44417                 }
44418
44419                 if (node === null) { return 'null'; }
44420
44421                 if (seen.indexOf(node) !== -1) {
44422                     if (cycles) { return JSON.stringify('__cycle__'); }
44423                     throw new TypeError('Converting circular structure to JSON');
44424                 }
44425
44426                 var seenIndex = seen.push(node) - 1;
44427                 var keys = Object.keys(node).sort(cmp && cmp(node));
44428                 out = '';
44429                 for (i = 0; i < keys.length; i++) {
44430                     var key = keys[i];
44431                     var value = stringify(node[key]);
44432
44433                     if (!value) { continue; }
44434                     if (out) { out += ','; }
44435                     out += JSON.stringify(key) + ':' + value;
44436                 }
44437                 seen.splice(seenIndex, 1);
44438                 return '{' + out + '}';
44439             })(data);
44440         };
44441
44442         function DEFAULT_COMPARE (a, b) { return a > b ? 1 : a < b ? -1 : 0; }
44443
44444         var SplayTree = function SplayTree(compare, noDuplicates) {
44445           if ( compare === void 0 ) compare = DEFAULT_COMPARE;
44446           if ( noDuplicates === void 0 ) noDuplicates = false;
44447
44448           this._compare = compare;
44449           this._root = null;
44450           this._size = 0;
44451           this._noDuplicates = !!noDuplicates;
44452         };
44453
44454         var prototypeAccessors = { size: { configurable: true } };
44455
44456
44457         SplayTree.prototype.rotateLeft = function rotateLeft (x) {
44458           var y = x.right;
44459           if (y) {
44460             x.right = y.left;
44461             if (y.left) { y.left.parent = x; }
44462             y.parent = x.parent;
44463           }
44464
44465           if (!x.parent)              { this._root = y; }
44466           else if (x === x.parent.left) { x.parent.left = y; }
44467           else                        { x.parent.right = y; }
44468           if (y) { y.left = x; }
44469           x.parent = y;
44470         };
44471
44472
44473         SplayTree.prototype.rotateRight = function rotateRight (x) {
44474           var y = x.left;
44475           if (y) {
44476             x.left = y.right;
44477             if (y.right) { y.right.parent = x; }
44478             y.parent = x.parent;
44479           }
44480
44481           if (!x.parent)             { this._root = y; }
44482           else if(x === x.parent.left) { x.parent.left = y; }
44483           else                       { x.parent.right = y; }
44484           if (y) { y.right = x; }
44485           x.parent = y;
44486         };
44487
44488
44489         SplayTree.prototype._splay = function _splay (x) {
44490           while (x.parent) {
44491             var p = x.parent;
44492             if (!p.parent) {
44493               if (p.left === x) { this.rotateRight(p); }
44494               else            { this.rotateLeft(p); }
44495             } else if (p.left === x && p.parent.left === p) {
44496               this.rotateRight(p.parent);
44497               this.rotateRight(p);
44498             } else if (p.right === x && p.parent.right === p) {
44499               this.rotateLeft(p.parent);
44500               this.rotateLeft(p);
44501             } else if (p.left === x && p.parent.right === p) {
44502               this.rotateRight(p);
44503               this.rotateLeft(p);
44504             } else {
44505               this.rotateLeft(p);
44506               this.rotateRight(p);
44507             }
44508           }
44509         };
44510
44511
44512         SplayTree.prototype.splay = function splay (x) {
44513           var p, gp, ggp, l, r;
44514
44515           while (x.parent) {
44516             p = x.parent;
44517             gp = p.parent;
44518
44519             if (gp && gp.parent) {
44520               ggp = gp.parent;
44521               if (ggp.left === gp) { ggp.left= x; }
44522               else               { ggp.right = x; }
44523               x.parent = ggp;
44524             } else {
44525               x.parent = null;
44526               this._root = x;
44527             }
44528
44529             l = x.left; r = x.right;
44530
44531             if (x === p.left) { // left
44532               if (gp) {
44533                 if (gp.left === p) {
44534                   /* zig-zig */
44535                   if (p.right) {
44536                     gp.left = p.right;
44537                     gp.left.parent = gp;
44538                   } else { gp.left = null; }
44539
44540                   p.right = gp;
44541                   gp.parent = p;
44542                 } else {
44543                   /* zig-zag */
44544                   if (l) {
44545                     gp.right = l;
44546                     l.parent = gp;
44547                   } else { gp.right = null; }
44548
44549                   x.left  = gp;
44550                   gp.parent = x;
44551                 }
44552               }
44553               if (r) {
44554                 p.left = r;
44555                 r.parent = p;
44556               } else { p.left = null; }
44557
44558               x.right= p;
44559               p.parent = x;
44560             } else { // right
44561               if (gp) {
44562                 if (gp.right === p) {
44563                   /* zig-zig */
44564                   if (p.left) {
44565                     gp.right = p.left;
44566                     gp.right.parent = gp;
44567                   } else { gp.right = null; }
44568
44569                   p.left = gp;
44570                   gp.parent = p;
44571                 } else {
44572                   /* zig-zag */
44573                   if (r) {
44574                     gp.left = r;
44575                     r.parent = gp;
44576                   } else { gp.left = null; }
44577
44578                   x.right = gp;
44579                   gp.parent = x;
44580                 }
44581               }
44582               if (l) {
44583                 p.right = l;
44584                 l.parent = p;
44585               } else { p.right = null; }
44586
44587               x.left = p;
44588               p.parent = x;
44589             }
44590           }
44591         };
44592
44593
44594         SplayTree.prototype.replace = function replace (u, v) {
44595           if (!u.parent) { this._root = v; }
44596           else if (u === u.parent.left) { u.parent.left = v; }
44597           else { u.parent.right = v; }
44598           if (v) { v.parent = u.parent; }
44599         };
44600
44601
44602         SplayTree.prototype.minNode = function minNode (u) {
44603             if ( u === void 0 ) u = this._root;
44604
44605           if (u) { while (u.left) { u = u.left; } }
44606           return u;
44607         };
44608
44609
44610         SplayTree.prototype.maxNode = function maxNode (u) {
44611             if ( u === void 0 ) u = this._root;
44612
44613           if (u) { while (u.right) { u = u.right; } }
44614           return u;
44615         };
44616
44617
44618         SplayTree.prototype.insert = function insert (key, data) {
44619           var z = this._root;
44620           var p = null;
44621           var comp = this._compare;
44622           var cmp;
44623
44624           if (this._noDuplicates) {
44625             while (z) {
44626               p = z;
44627               cmp = comp(z.key, key);
44628               if (cmp === 0) { return; }
44629               else if (comp(z.key, key) < 0) { z = z.right; }
44630               else { z = z.left; }
44631             }
44632           } else {
44633             while (z) {
44634               p = z;
44635               if (comp(z.key, key) < 0) { z = z.right; }
44636               else { z = z.left; }
44637             }
44638           }
44639
44640           z = { key: key, data: data, left: null, right: null, parent: p };
44641
44642           if (!p)                        { this._root = z; }
44643           else if (comp(p.key, z.key) < 0) { p.right = z; }
44644           else                           { p.left= z; }
44645
44646           this.splay(z);
44647           this._size++;
44648           return z;
44649         };
44650
44651
44652         SplayTree.prototype.find = function find (key) {
44653           var z  = this._root;
44654           var comp = this._compare;
44655           while (z) {
44656             var cmp = comp(z.key, key);
44657             if    (cmp < 0) { z = z.right; }
44658             else if (cmp > 0) { z = z.left; }
44659             else            { return z; }
44660           }
44661           return null;
44662         };
44663
44664         /**
44665          * Whether the tree contains a node with the given key
44666          * @param{Key} key
44667          * @return {boolean} true/false
44668          */
44669         SplayTree.prototype.contains = function contains (key) {
44670           var node     = this._root;
44671           var comparator = this._compare;
44672           while (node){
44673             var cmp = comparator(key, node.key);
44674             if    (cmp === 0) { return true; }
44675             else if (cmp < 0) { node = node.left; }
44676             else              { node = node.right; }
44677           }
44678
44679           return false;
44680         };
44681
44682
44683         SplayTree.prototype.remove = function remove (key) {
44684           var z = this.find(key);
44685
44686           if (!z) { return false; }
44687
44688           this.splay(z);
44689
44690           if (!z.left) { this.replace(z, z.right); }
44691           else if (!z.right) { this.replace(z, z.left); }
44692           else {
44693             var y = this.minNode(z.right);
44694             if (y.parent !== z) {
44695               this.replace(y, y.right);
44696               y.right = z.right;
44697               y.right.parent = y;
44698             }
44699             this.replace(z, y);
44700             y.left = z.left;
44701             y.left.parent = y;
44702           }
44703
44704           this._size--;
44705           return true;
44706         };
44707
44708
44709         SplayTree.prototype.removeNode = function removeNode (z) {
44710           if (!z) { return false; }
44711
44712           this.splay(z);
44713
44714           if (!z.left) { this.replace(z, z.right); }
44715           else if (!z.right) { this.replace(z, z.left); }
44716           else {
44717             var y = this.minNode(z.right);
44718             if (y.parent !== z) {
44719               this.replace(y, y.right);
44720               y.right = z.right;
44721               y.right.parent = y;
44722             }
44723             this.replace(z, y);
44724             y.left = z.left;
44725             y.left.parent = y;
44726           }
44727
44728           this._size--;
44729           return true;
44730         };
44731
44732
44733         SplayTree.prototype.erase = function erase (key) {
44734           var z = this.find(key);
44735           if (!z) { return; }
44736
44737           this.splay(z);
44738
44739           var s = z.left;
44740           var t = z.right;
44741
44742           var sMax = null;
44743           if (s) {
44744             s.parent = null;
44745             sMax = this.maxNode(s);
44746             this.splay(sMax);
44747             this._root = sMax;
44748           }
44749           if (t) {
44750             if (s) { sMax.right = t; }
44751             else { this._root = t; }
44752             t.parent = sMax;
44753           }
44754
44755           this._size--;
44756         };
44757
44758         /**
44759          * Removes and returns the node with smallest key
44760          * @return {?Node}
44761          */
44762         SplayTree.prototype.pop = function pop () {
44763           var node = this._root, returnValue = null;
44764           if (node) {
44765             while (node.left) { node = node.left; }
44766             returnValue = { key: node.key, data: node.data };
44767             this.remove(node.key);
44768           }
44769           return returnValue;
44770         };
44771
44772
44773         /* eslint-disable class-methods-use-this */
44774
44775         /**
44776          * Successor node
44777          * @param{Node} node
44778          * @return {?Node}
44779          */
44780         SplayTree.prototype.next = function next (node) {
44781           var successor = node;
44782           if (successor) {
44783             if (successor.right) {
44784               successor = successor.right;
44785               while (successor && successor.left) { successor = successor.left; }
44786             } else {
44787               successor = node.parent;
44788               while (successor && successor.right === node) {
44789                 node = successor; successor = successor.parent;
44790               }
44791             }
44792           }
44793           return successor;
44794         };
44795
44796
44797         /**
44798          * Predecessor node
44799          * @param{Node} node
44800          * @return {?Node}
44801          */
44802         SplayTree.prototype.prev = function prev (node) {
44803           var predecessor = node;
44804           if (predecessor) {
44805             if (predecessor.left) {
44806               predecessor = predecessor.left;
44807               while (predecessor && predecessor.right) { predecessor = predecessor.right; }
44808             } else {
44809               predecessor = node.parent;
44810               while (predecessor && predecessor.left === node) {
44811                 node = predecessor;
44812                 predecessor = predecessor.parent;
44813               }
44814             }
44815           }
44816           return predecessor;
44817         };
44818         /* eslint-enable class-methods-use-this */
44819
44820
44821         /**
44822          * @param{forEachCallback} callback
44823          * @return {SplayTree}
44824          */
44825         SplayTree.prototype.forEach = function forEach (callback) {
44826           var current = this._root;
44827           var s = [], done = false, i = 0;
44828
44829           while (!done) {
44830             // Reach the left most Node of the current Node
44831             if (current) {
44832               // Place pointer to a tree node on the stack
44833               // before traversing the node's left subtree
44834               s.push(current);
44835               current = current.left;
44836             } else {
44837               // BackTrack from the empty subtree and visit the Node
44838               // at the top of the stack; however, if the stack is
44839               // empty you are done
44840               if (s.length > 0) {
44841                 current = s.pop();
44842                 callback(current, i++);
44843
44844                 // We have visited the node and its left
44845                 // subtree. Now, it's right subtree's turn
44846                 current = current.right;
44847               } else { done = true; }
44848             }
44849           }
44850           return this;
44851         };
44852
44853
44854         /**
44855          * Walk key range from `low` to `high`. Stops if `fn` returns a value.
44856          * @param{Key}    low
44857          * @param{Key}    high
44858          * @param{Function} fn
44859          * @param{*?}     ctx
44860          * @return {SplayTree}
44861          */
44862         SplayTree.prototype.range = function range (low, high, fn, ctx) {
44863           var Q = [];
44864           var compare = this._compare;
44865           var node = this._root, cmp;
44866
44867           while (Q.length !== 0 || node) {
44868             if (node) {
44869               Q.push(node);
44870               node = node.left;
44871             } else {
44872               node = Q.pop();
44873               cmp = compare(node.key, high);
44874               if (cmp > 0) {
44875                 break;
44876               } else if (compare(node.key, low) >= 0) {
44877                 if (fn.call(ctx, node)) { return this; } // stop if smth is returned
44878               }
44879               node = node.right;
44880             }
44881           }
44882           return this;
44883         };
44884
44885         /**
44886          * Returns all keys in order
44887          * @return {Array<Key>}
44888          */
44889         SplayTree.prototype.keys = function keys () {
44890           var current = this._root;
44891           var s = [], r = [], done = false;
44892
44893           while (!done) {
44894             if (current) {
44895               s.push(current);
44896               current = current.left;
44897             } else {
44898               if (s.length > 0) {
44899                 current = s.pop();
44900                 r.push(current.key);
44901                 current = current.right;
44902               } else { done = true; }
44903             }
44904           }
44905           return r;
44906         };
44907
44908
44909         /**
44910          * Returns `data` fields of all nodes in order.
44911          * @return {Array<Value>}
44912          */
44913         SplayTree.prototype.values = function values () {
44914           var current = this._root;
44915           var s = [], r = [], done = false;
44916
44917           while (!done) {
44918             if (current) {
44919               s.push(current);
44920               current = current.left;
44921             } else {
44922               if (s.length > 0) {
44923                 current = s.pop();
44924                 r.push(current.data);
44925                 current = current.right;
44926               } else { done = true; }
44927             }
44928           }
44929           return r;
44930         };
44931
44932
44933         /**
44934          * Returns node at given index
44935          * @param{number} index
44936          * @return {?Node}
44937          */
44938         SplayTree.prototype.at = function at (index) {
44939           // removed after a consideration, more misleading than useful
44940           // index = index % this.size;
44941           // if (index < 0) index = this.size - index;
44942
44943           var current = this._root;
44944           var s = [], done = false, i = 0;
44945
44946           while (!done) {
44947             if (current) {
44948               s.push(current);
44949               current = current.left;
44950             } else {
44951               if (s.length > 0) {
44952                 current = s.pop();
44953                 if (i === index) { return current; }
44954                 i++;
44955                 current = current.right;
44956               } else { done = true; }
44957             }
44958           }
44959           return null;
44960         };
44961
44962         /**
44963          * Bulk-load items. Both array have to be same size
44964          * @param{Array<Key>}  keys
44965          * @param{Array<Value>}[values]
44966          * @param{Boolean}     [presort=false] Pre-sort keys and values, using
44967          *                                       tree's comparator. Sorting is done
44968          *                                       in-place
44969          * @return {AVLTree}
44970          */
44971         SplayTree.prototype.load = function load (keys, values, presort) {
44972             if ( keys === void 0 ) keys = [];
44973             if ( values === void 0 ) values = [];
44974             if ( presort === void 0 ) presort = false;
44975
44976           if (this._size !== 0) { throw new Error('bulk-load: tree is not empty'); }
44977           var size = keys.length;
44978           if (presort) { sort(keys, values, 0, size - 1, this._compare); }
44979           this._root = loadRecursive(null, keys, values, 0, size);
44980           this._size = size;
44981           return this;
44982         };
44983
44984
44985         SplayTree.prototype.min = function min () {
44986           var node = this.minNode(this._root);
44987           if (node) { return node.key; }
44988           else    { return null; }
44989         };
44990
44991
44992         SplayTree.prototype.max = function max () {
44993           var node = this.maxNode(this._root);
44994           if (node) { return node.key; }
44995           else    { return null; }
44996         };
44997
44998         SplayTree.prototype.isEmpty = function isEmpty () { return this._root === null; };
44999         prototypeAccessors.size.get = function () { return this._size; };
45000
45001
45002         /**
45003          * Create a tree and load it with items
45004          * @param{Array<Key>}        keys
45005          * @param{Array<Value>?}      [values]
45006
45007          * @param{Function?}          [comparator]
45008          * @param{Boolean?}           [presort=false] Pre-sort keys and values, using
45009          *                                             tree's comparator. Sorting is done
45010          *                                             in-place
45011          * @param{Boolean?}           [noDuplicates=false] Allow duplicates
45012          * @return {SplayTree}
45013          */
45014         SplayTree.createTree = function createTree (keys, values, comparator, presort, noDuplicates) {
45015           return new SplayTree(comparator, noDuplicates).load(keys, values, presort);
45016         };
45017
45018         Object.defineProperties( SplayTree.prototype, prototypeAccessors );
45019
45020
45021         function loadRecursive (parent, keys, values, start, end) {
45022           var size = end - start;
45023           if (size > 0) {
45024             var middle = start + Math.floor(size / 2);
45025             var key    = keys[middle];
45026             var data   = values[middle];
45027             var node   = { key: key, data: data, parent: parent };
45028             node.left    = loadRecursive(node, keys, values, start, middle);
45029             node.right   = loadRecursive(node, keys, values, middle + 1, end);
45030             return node;
45031           }
45032           return null;
45033         }
45034
45035
45036         function sort(keys, values, left, right, compare) {
45037           if (left >= right) { return; }
45038
45039           var pivot = keys[(left + right) >> 1];
45040           var i = left - 1;
45041           var j = right + 1;
45042
45043           while (true) {
45044             do { i++; } while (compare(keys[i], pivot) < 0);
45045             do { j--; } while (compare(keys[j], pivot) > 0);
45046             if (i >= j) { break; }
45047
45048             var tmp = keys[i];
45049             keys[i] = keys[j];
45050             keys[j] = tmp;
45051
45052             tmp = values[i];
45053             values[i] = values[j];
45054             values[j] = tmp;
45055           }
45056
45057           sort(keys, values,  left,     j, compare);
45058           sort(keys, values, j + 1, right, compare);
45059         }
45060
45061         var NORMAL               = 0;
45062         var NON_CONTRIBUTING     = 1;
45063         var SAME_TRANSITION      = 2;
45064         var DIFFERENT_TRANSITION = 3;
45065
45066         var INTERSECTION = 0;
45067         var UNION        = 1;
45068         var DIFFERENCE   = 2;
45069         var XOR          = 3;
45070
45071         /**
45072          * @param  {SweepEvent} event
45073          * @param  {SweepEvent} prev
45074          * @param  {Operation} operation
45075          */
45076         function computeFields (event, prev, operation) {
45077           // compute inOut and otherInOut fields
45078           if (prev === null) {
45079             event.inOut      = false;
45080             event.otherInOut = true;
45081
45082           // previous line segment in sweepline belongs to the same polygon
45083           } else {
45084             if (event.isSubject === prev.isSubject) {
45085               event.inOut      = !prev.inOut;
45086               event.otherInOut = prev.otherInOut;
45087
45088             // previous line segment in sweepline belongs to the clipping polygon
45089             } else {
45090               event.inOut      = !prev.otherInOut;
45091               event.otherInOut = prev.isVertical() ? !prev.inOut : prev.inOut;
45092             }
45093
45094             // compute prevInResult field
45095             if (prev) {
45096               event.prevInResult = (!inResult(prev, operation) || prev.isVertical())
45097                 ? prev.prevInResult : prev;
45098             }
45099           }
45100
45101           // check if the line segment belongs to the Boolean operation
45102           var isInResult = inResult(event, operation);
45103           if (isInResult) {
45104             event.resultTransition = determineResultTransition(event, operation);
45105           } else {
45106             event.resultTransition = 0;
45107           }
45108         }
45109
45110
45111         /* eslint-disable indent */
45112         function inResult(event, operation) {
45113           switch (event.type) {
45114             case NORMAL:
45115               switch (operation) {
45116                 case INTERSECTION:
45117                   return !event.otherInOut;
45118                 case UNION:
45119                   return event.otherInOut;
45120                 case DIFFERENCE:
45121                   // return (event.isSubject && !event.otherInOut) ||
45122                   //         (!event.isSubject && event.otherInOut);
45123                   return (event.isSubject && event.otherInOut) ||
45124                           (!event.isSubject && !event.otherInOut);
45125                 case XOR:
45126                   return true;
45127               }
45128               break;
45129             case SAME_TRANSITION:
45130               return operation === INTERSECTION || operation === UNION;
45131             case DIFFERENT_TRANSITION:
45132               return operation === DIFFERENCE;
45133             case NON_CONTRIBUTING:
45134               return false;
45135           }
45136           return false;
45137         }
45138         /* eslint-enable indent */
45139
45140
45141         function determineResultTransition(event, operation) {
45142           var thisIn = !event.inOut;
45143           var thatIn = !event.otherInOut;
45144
45145           var isIn;
45146           switch (operation) {
45147             case INTERSECTION:
45148               isIn = thisIn && thatIn; break;
45149             case UNION:
45150               isIn = thisIn || thatIn; break;
45151             case XOR:
45152               isIn = thisIn ^ thatIn; break;
45153             case DIFFERENCE:
45154               if (event.isSubject) {
45155                 isIn = thisIn && !thatIn;
45156               } else {
45157                 isIn = thatIn && !thisIn;
45158               }
45159               break;
45160           }
45161           return isIn ? +1 : -1;
45162         }
45163
45164         var SweepEvent = function SweepEvent (point, left, otherEvent, isSubject, edgeType) {
45165
45166           /**
45167            * Is left endpoint?
45168            * @type {Boolean}
45169            */
45170           this.left = left;
45171
45172           /**
45173            * @type {Array.<Number>}
45174            */
45175           this.point = point;
45176
45177           /**
45178            * Other edge reference
45179            * @type {SweepEvent}
45180            */
45181           this.otherEvent = otherEvent;
45182
45183           /**
45184            * Belongs to source or clipping polygon
45185            * @type {Boolean}
45186            */
45187           this.isSubject = isSubject;
45188
45189           /**
45190            * Edge contribution type
45191            * @type {Number}
45192            */
45193           this.type = edgeType || NORMAL;
45194
45195
45196           /**
45197            * In-out transition for the sweepline crossing polygon
45198            * @type {Boolean}
45199            */
45200           this.inOut = false;
45201
45202
45203           /**
45204            * @type {Boolean}
45205            */
45206           this.otherInOut = false;
45207
45208           /**
45209            * Previous event in result?
45210            * @type {SweepEvent}
45211            */
45212           this.prevInResult = null;
45213
45214           /**
45215            * Type of result transition (0 = not in result, +1 = out-in, -1, in-out)
45216            * @type {Number}
45217            */
45218           this.resultTransition = 0;
45219
45220           // connection step
45221
45222           /**
45223            * @type {Number}
45224            */
45225           this.otherPos = -1;
45226
45227           /**
45228            * @type {Number}
45229            */
45230           this.outputContourId = -1;
45231
45232           this.isExteriorRing = true; // TODO: Looks unused, remove?
45233         };
45234
45235         var prototypeAccessors$1 = { inResult: { configurable: true } };
45236
45237
45238         /**
45239          * @param{Array.<Number>}p
45240          * @return {Boolean}
45241          */
45242         SweepEvent.prototype.isBelow = function isBelow (p) {
45243           var p0 = this.point, p1 = this.otherEvent.point;
45244           return this.left
45245             ? (p0[0] - p[0]) * (p1[1] - p[1]) - (p1[0] - p[0]) * (p0[1] - p[1]) > 0
45246             // signedArea(this.point, this.otherEvent.point, p) > 0 :
45247             : (p1[0] - p[0]) * (p0[1] - p[1]) - (p0[0] - p[0]) * (p1[1] - p[1]) > 0;
45248             //signedArea(this.otherEvent.point, this.point, p) > 0;
45249         };
45250
45251
45252         /**
45253          * @param{Array.<Number>}p
45254          * @return {Boolean}
45255          */
45256         SweepEvent.prototype.isAbove = function isAbove (p) {
45257           return !this.isBelow(p);
45258         };
45259
45260
45261         /**
45262          * @return {Boolean}
45263          */
45264         SweepEvent.prototype.isVertical = function isVertical () {
45265           return this.point[0] === this.otherEvent.point[0];
45266         };
45267
45268
45269         /**
45270          * Does event belong to result?
45271          * @return {Boolean}
45272          */
45273         prototypeAccessors$1.inResult.get = function () {
45274           return this.resultTransition !== 0;
45275         };
45276
45277
45278         SweepEvent.prototype.clone = function clone () {
45279           var copy = new SweepEvent(
45280             this.point, this.left, this.otherEvent, this.isSubject, this.type);
45281
45282           copy.contourId      = this.contourId;
45283           copy.resultTransition = this.resultTransition;
45284           copy.prevInResult   = this.prevInResult;
45285           copy.isExteriorRing = this.isExteriorRing;
45286           copy.inOut          = this.inOut;
45287           copy.otherInOut     = this.otherInOut;
45288
45289           return copy;
45290         };
45291
45292         Object.defineProperties( SweepEvent.prototype, prototypeAccessors$1 );
45293
45294         function equals(p1, p2) {
45295           if (p1[0] === p2[0]) {
45296             if (p1[1] === p2[1]) {
45297               return true;
45298             } else {
45299               return false;
45300             }
45301           }
45302           return false;
45303         }
45304
45305         // const EPSILON = 1e-9;
45306         // const abs = Math.abs;
45307         // TODO https://github.com/w8r/martinez/issues/6#issuecomment-262847164
45308         // Precision problem.
45309         //
45310         // module.exports = function equals(p1, p2) {
45311         //   return abs(p1[0] - p2[0]) <= EPSILON && abs(p1[1] - p2[1]) <= EPSILON;
45312         // };
45313
45314         var epsilon$1 = 1.1102230246251565e-16;
45315         var splitter = 134217729;
45316         var resulterrbound = (3 + 8 * epsilon$1) * epsilon$1;
45317
45318         // fast_expansion_sum_zeroelim routine from oritinal code
45319         function sum$1(elen, e, flen, f, h) {
45320             var Q, Qnew, hh, bvirt;
45321             var enow = e[0];
45322             var fnow = f[0];
45323             var eindex = 0;
45324             var findex = 0;
45325             if ((fnow > enow) === (fnow > -enow)) {
45326                 Q = enow;
45327                 enow = e[++eindex];
45328             } else {
45329                 Q = fnow;
45330                 fnow = f[++findex];
45331             }
45332             var hindex = 0;
45333             if (eindex < elen && findex < flen) {
45334                 if ((fnow > enow) === (fnow > -enow)) {
45335                     Qnew = enow + Q;
45336                     hh = Q - (Qnew - enow);
45337                     enow = e[++eindex];
45338                 } else {
45339                     Qnew = fnow + Q;
45340                     hh = Q - (Qnew - fnow);
45341                     fnow = f[++findex];
45342                 }
45343                 Q = Qnew;
45344                 if (hh !== 0) {
45345                     h[hindex++] = hh;
45346                 }
45347                 while (eindex < elen && findex < flen) {
45348                     if ((fnow > enow) === (fnow > -enow)) {
45349                         Qnew = Q + enow;
45350                         bvirt = Qnew - Q;
45351                         hh = Q - (Qnew - bvirt) + (enow - bvirt);
45352                         enow = e[++eindex];
45353                     } else {
45354                         Qnew = Q + fnow;
45355                         bvirt = Qnew - Q;
45356                         hh = Q - (Qnew - bvirt) + (fnow - bvirt);
45357                         fnow = f[++findex];
45358                     }
45359                     Q = Qnew;
45360                     if (hh !== 0) {
45361                         h[hindex++] = hh;
45362                     }
45363                 }
45364             }
45365             while (eindex < elen) {
45366                 Qnew = Q + enow;
45367                 bvirt = Qnew - Q;
45368                 hh = Q - (Qnew - bvirt) + (enow - bvirt);
45369                 enow = e[++eindex];
45370                 Q = Qnew;
45371                 if (hh !== 0) {
45372                     h[hindex++] = hh;
45373                 }
45374             }
45375             while (findex < flen) {
45376                 Qnew = Q + fnow;
45377                 bvirt = Qnew - Q;
45378                 hh = Q - (Qnew - bvirt) + (fnow - bvirt);
45379                 fnow = f[++findex];
45380                 Q = Qnew;
45381                 if (hh !== 0) {
45382                     h[hindex++] = hh;
45383                 }
45384             }
45385             if (Q !== 0 || hindex === 0) {
45386                 h[hindex++] = Q;
45387             }
45388             return hindex;
45389         }
45390
45391         function estimate(elen, e) {
45392             var Q = e[0];
45393             for (var i = 1; i < elen; i++) { Q += e[i]; }
45394             return Q;
45395         }
45396
45397         function vec(n) {
45398             return new Float64Array(n);
45399         }
45400
45401         var ccwerrboundA = (3 + 16 * epsilon$1) * epsilon$1;
45402         var ccwerrboundB = (2 + 12 * epsilon$1) * epsilon$1;
45403         var ccwerrboundC = (9 + 64 * epsilon$1) * epsilon$1 * epsilon$1;
45404
45405         var B = vec(4);
45406         var C1 = vec(8);
45407         var C2 = vec(12);
45408         var D = vec(16);
45409         var u = vec(4);
45410
45411         function orient2dadapt(ax, ay, bx, by, cx, cy, detsum) {
45412             var acxtail, acytail, bcxtail, bcytail;
45413             var bvirt, c, ahi, alo, bhi, blo, _i, _j, _0, s1, s0, t1, t0, u3;
45414
45415             var acx = ax - cx;
45416             var bcx = bx - cx;
45417             var acy = ay - cy;
45418             var bcy = by - cy;
45419
45420             s1 = acx * bcy;
45421             c = splitter * acx;
45422             ahi = c - (c - acx);
45423             alo = acx - ahi;
45424             c = splitter * bcy;
45425             bhi = c - (c - bcy);
45426             blo = bcy - bhi;
45427             s0 = alo * blo - (s1 - ahi * bhi - alo * bhi - ahi * blo);
45428             t1 = acy * bcx;
45429             c = splitter * acy;
45430             ahi = c - (c - acy);
45431             alo = acy - ahi;
45432             c = splitter * bcx;
45433             bhi = c - (c - bcx);
45434             blo = bcx - bhi;
45435             t0 = alo * blo - (t1 - ahi * bhi - alo * bhi - ahi * blo);
45436             _i = s0 - t0;
45437             bvirt = s0 - _i;
45438             B[0] = s0 - (_i + bvirt) + (bvirt - t0);
45439             _j = s1 + _i;
45440             bvirt = _j - s1;
45441             _0 = s1 - (_j - bvirt) + (_i - bvirt);
45442             _i = _0 - t1;
45443             bvirt = _0 - _i;
45444             B[1] = _0 - (_i + bvirt) + (bvirt - t1);
45445             u3 = _j + _i;
45446             bvirt = u3 - _j;
45447             B[2] = _j - (u3 - bvirt) + (_i - bvirt);
45448             B[3] = u3;
45449
45450             var det = estimate(4, B);
45451             var errbound = ccwerrboundB * detsum;
45452             if (det >= errbound || -det >= errbound) {
45453                 return det;
45454             }
45455
45456             bvirt = ax - acx;
45457             acxtail = ax - (acx + bvirt) + (bvirt - cx);
45458             bvirt = bx - bcx;
45459             bcxtail = bx - (bcx + bvirt) + (bvirt - cx);
45460             bvirt = ay - acy;
45461             acytail = ay - (acy + bvirt) + (bvirt - cy);
45462             bvirt = by - bcy;
45463             bcytail = by - (bcy + bvirt) + (bvirt - cy);
45464
45465             if (acxtail === 0 && acytail === 0 && bcxtail === 0 && bcytail === 0) {
45466                 return det;
45467             }
45468
45469             errbound = ccwerrboundC * detsum + resulterrbound * Math.abs(det);
45470             det += (acx * bcytail + bcy * acxtail) - (acy * bcxtail + bcx * acytail);
45471             if (det >= errbound || -det >= errbound) { return det; }
45472
45473             s1 = acxtail * bcy;
45474             c = splitter * acxtail;
45475             ahi = c - (c - acxtail);
45476             alo = acxtail - ahi;
45477             c = splitter * bcy;
45478             bhi = c - (c - bcy);
45479             blo = bcy - bhi;
45480             s0 = alo * blo - (s1 - ahi * bhi - alo * bhi - ahi * blo);
45481             t1 = acytail * bcx;
45482             c = splitter * acytail;
45483             ahi = c - (c - acytail);
45484             alo = acytail - ahi;
45485             c = splitter * bcx;
45486             bhi = c - (c - bcx);
45487             blo = bcx - bhi;
45488             t0 = alo * blo - (t1 - ahi * bhi - alo * bhi - ahi * blo);
45489             _i = s0 - t0;
45490             bvirt = s0 - _i;
45491             u[0] = s0 - (_i + bvirt) + (bvirt - t0);
45492             _j = s1 + _i;
45493             bvirt = _j - s1;
45494             _0 = s1 - (_j - bvirt) + (_i - bvirt);
45495             _i = _0 - t1;
45496             bvirt = _0 - _i;
45497             u[1] = _0 - (_i + bvirt) + (bvirt - t1);
45498             u3 = _j + _i;
45499             bvirt = u3 - _j;
45500             u[2] = _j - (u3 - bvirt) + (_i - bvirt);
45501             u[3] = u3;
45502             var C1len = sum$1(4, B, 4, u, C1);
45503
45504             s1 = acx * bcytail;
45505             c = splitter * acx;
45506             ahi = c - (c - acx);
45507             alo = acx - ahi;
45508             c = splitter * bcytail;
45509             bhi = c - (c - bcytail);
45510             blo = bcytail - bhi;
45511             s0 = alo * blo - (s1 - ahi * bhi - alo * bhi - ahi * blo);
45512             t1 = acy * bcxtail;
45513             c = splitter * acy;
45514             ahi = c - (c - acy);
45515             alo = acy - ahi;
45516             c = splitter * bcxtail;
45517             bhi = c - (c - bcxtail);
45518             blo = bcxtail - bhi;
45519             t0 = alo * blo - (t1 - ahi * bhi - alo * bhi - ahi * blo);
45520             _i = s0 - t0;
45521             bvirt = s0 - _i;
45522             u[0] = s0 - (_i + bvirt) + (bvirt - t0);
45523             _j = s1 + _i;
45524             bvirt = _j - s1;
45525             _0 = s1 - (_j - bvirt) + (_i - bvirt);
45526             _i = _0 - t1;
45527             bvirt = _0 - _i;
45528             u[1] = _0 - (_i + bvirt) + (bvirt - t1);
45529             u3 = _j + _i;
45530             bvirt = u3 - _j;
45531             u[2] = _j - (u3 - bvirt) + (_i - bvirt);
45532             u[3] = u3;
45533             var C2len = sum$1(C1len, C1, 4, u, C2);
45534
45535             s1 = acxtail * bcytail;
45536             c = splitter * acxtail;
45537             ahi = c - (c - acxtail);
45538             alo = acxtail - ahi;
45539             c = splitter * bcytail;
45540             bhi = c - (c - bcytail);
45541             blo = bcytail - bhi;
45542             s0 = alo * blo - (s1 - ahi * bhi - alo * bhi - ahi * blo);
45543             t1 = acytail * bcxtail;
45544             c = splitter * acytail;
45545             ahi = c - (c - acytail);
45546             alo = acytail - ahi;
45547             c = splitter * bcxtail;
45548             bhi = c - (c - bcxtail);
45549             blo = bcxtail - bhi;
45550             t0 = alo * blo - (t1 - ahi * bhi - alo * bhi - ahi * blo);
45551             _i = s0 - t0;
45552             bvirt = s0 - _i;
45553             u[0] = s0 - (_i + bvirt) + (bvirt - t0);
45554             _j = s1 + _i;
45555             bvirt = _j - s1;
45556             _0 = s1 - (_j - bvirt) + (_i - bvirt);
45557             _i = _0 - t1;
45558             bvirt = _0 - _i;
45559             u[1] = _0 - (_i + bvirt) + (bvirt - t1);
45560             u3 = _j + _i;
45561             bvirt = u3 - _j;
45562             u[2] = _j - (u3 - bvirt) + (_i - bvirt);
45563             u[3] = u3;
45564             var Dlen = sum$1(C2len, C2, 4, u, D);
45565
45566             return D[Dlen - 1];
45567         }
45568
45569         function orient2d(ax, ay, bx, by, cx, cy) {
45570             var detleft = (ay - cy) * (bx - cx);
45571             var detright = (ax - cx) * (by - cy);
45572             var det = detleft - detright;
45573
45574             if (detleft === 0 || detright === 0 || (detleft > 0) !== (detright > 0)) { return det; }
45575
45576             var detsum = Math.abs(detleft + detright);
45577             if (Math.abs(det) >= ccwerrboundA * detsum) { return det; }
45578
45579             return -orient2dadapt(ax, ay, bx, by, cx, cy, detsum);
45580         }
45581
45582         /**
45583          * Signed area of the triangle (p0, p1, p2)
45584          * @param  {Array.<Number>} p0
45585          * @param  {Array.<Number>} p1
45586          * @param  {Array.<Number>} p2
45587          * @return {Number}
45588          */
45589         function signedArea(p0, p1, p2) {
45590           var res = orient2d(p0[0], p0[1], p1[0], p1[1], p2[0], p2[1]);
45591           if (res > 0) { return -1; }
45592           if (res < 0) { return 1; }
45593           return 0;
45594         }
45595
45596         /**
45597          * @param  {SweepEvent} e1
45598          * @param  {SweepEvent} e2
45599          * @return {Number}
45600          */
45601         function compareEvents(e1, e2) {
45602           var p1 = e1.point;
45603           var p2 = e2.point;
45604
45605           // Different x-coordinate
45606           if (p1[0] > p2[0]) { return 1; }
45607           if (p1[0] < p2[0]) { return -1; }
45608
45609           // Different points, but same x-coordinate
45610           // Event with lower y-coordinate is processed first
45611           if (p1[1] !== p2[1]) { return p1[1] > p2[1] ? 1 : -1; }
45612
45613           return specialCases(e1, e2, p1);
45614         }
45615
45616
45617         /* eslint-disable no-unused-vars */
45618         function specialCases(e1, e2, p1, p2) {
45619           // Same coordinates, but one is a left endpoint and the other is
45620           // a right endpoint. The right endpoint is processed first
45621           if (e1.left !== e2.left)
45622             { return e1.left ? 1 : -1; }
45623
45624           // const p2 = e1.otherEvent.point, p3 = e2.otherEvent.point;
45625           // const sa = (p1[0] - p3[0]) * (p2[1] - p3[1]) - (p2[0] - p3[0]) * (p1[1] - p3[1])
45626           // Same coordinates, both events
45627           // are left endpoints or right endpoints.
45628           // not collinear
45629           if (signedArea(p1, e1.otherEvent.point, e2.otherEvent.point) !== 0) {
45630             // the event associate to the bottom segment is processed first
45631             return (!e1.isBelow(e2.otherEvent.point)) ? 1 : -1;
45632           }
45633
45634           return (!e1.isSubject && e2.isSubject) ? 1 : -1;
45635         }
45636         /* eslint-enable no-unused-vars */
45637
45638         /**
45639          * @param  {SweepEvent} se
45640          * @param  {Array.<Number>} p
45641          * @param  {Queue} queue
45642          * @return {Queue}
45643          */
45644         function divideSegment(se, p, queue)  {
45645           var r = new SweepEvent(p, false, se,            se.isSubject);
45646           var l = new SweepEvent(p, true,  se.otherEvent, se.isSubject);
45647
45648           /* eslint-disable no-console */
45649           if (equals(se.point, se.otherEvent.point)) {
45650             console.warn('what is that, a collapsed segment?', se);
45651           }
45652           /* eslint-enable no-console */
45653
45654           r.contourId = l.contourId = se.contourId;
45655
45656           // avoid a rounding error. The left event would be processed after the right event
45657           if (compareEvents(l, se.otherEvent) > 0) {
45658             se.otherEvent.left = true;
45659             l.left = false;
45660           }
45661
45662           // avoid a rounding error. The left event would be processed after the right event
45663           // if (compareEvents(se, r) > 0) {}
45664
45665           se.otherEvent.otherEvent = l;
45666           se.otherEvent = r;
45667
45668           queue.push(l);
45669           queue.push(r);
45670
45671           return queue;
45672         }
45673
45674         //const EPS = 1e-9;
45675
45676         /**
45677          * Finds the magnitude of the cross product of two vectors (if we pretend
45678          * they're in three dimensions)
45679          *
45680          * @param {Object} a First vector
45681          * @param {Object} b Second vector
45682          * @private
45683          * @returns {Number} The magnitude of the cross product
45684          */
45685         function crossProduct(a, b) {
45686           return (a[0] * b[1]) - (a[1] * b[0]);
45687         }
45688
45689         /**
45690          * Finds the dot product of two vectors.
45691          *
45692          * @param {Object} a First vector
45693          * @param {Object} b Second vector
45694          * @private
45695          * @returns {Number} The dot product
45696          */
45697         function dotProduct(a, b) {
45698           return (a[0] * b[0]) + (a[1] * b[1]);
45699         }
45700
45701         /**
45702          * Finds the intersection (if any) between two line segments a and b, given the
45703          * line segments' end points a1, a2 and b1, b2.
45704          *
45705          * This algorithm is based on Schneider and Eberly.
45706          * http://www.cimec.org.ar/~ncalvo/Schneider_Eberly.pdf
45707          * Page 244.
45708          *
45709          * @param {Array.<Number>} a1 point of first line
45710          * @param {Array.<Number>} a2 point of first line
45711          * @param {Array.<Number>} b1 point of second line
45712          * @param {Array.<Number>} b2 point of second line
45713          * @param {Boolean=}       noEndpointTouch whether to skip single touchpoints
45714          *                                         (meaning connected segments) as
45715          *                                         intersections
45716          * @returns {Array.<Array.<Number>>|Null} If the lines intersect, the point of
45717          * intersection. If they overlap, the two end points of the overlapping segment.
45718          * Otherwise, null.
45719          */
45720         function intersection (a1, a2, b1, b2, noEndpointTouch) {
45721           // The algorithm expects our lines in the form P + sd, where P is a point,
45722           // s is on the interval [0, 1], and d is a vector.
45723           // We are passed two points. P can be the first point of each pair. The
45724           // vector, then, could be thought of as the distance (in x and y components)
45725           // from the first point to the second point.
45726           // So first, let's make our vectors:
45727           var va = [a2[0] - a1[0], a2[1] - a1[1]];
45728           var vb = [b2[0] - b1[0], b2[1] - b1[1]];
45729           // We also define a function to convert back to regular point form:
45730
45731           /* eslint-disable arrow-body-style */
45732
45733           function toPoint(p, s, d) {
45734             return [
45735               p[0] + s * d[0],
45736               p[1] + s * d[1]
45737             ];
45738           }
45739
45740           /* eslint-enable arrow-body-style */
45741
45742           // The rest is pretty much a straight port of the algorithm.
45743           var e = [b1[0] - a1[0], b1[1] - a1[1]];
45744           var kross    = crossProduct(va, vb);
45745           var sqrKross = kross * kross;
45746           var sqrLenA  = dotProduct(va, va);
45747           //const sqrLenB  = dotProduct(vb, vb);
45748
45749           // Check for line intersection. This works because of the properties of the
45750           // cross product -- specifically, two vectors are parallel if and only if the
45751           // cross product is the 0 vector. The full calculation involves relative error
45752           // to account for possible very small line segments. See Schneider & Eberly
45753           // for details.
45754           if (sqrKross > 0/* EPS * sqrLenB * sqLenA */) {
45755             // If they're not parallel, then (because these are line segments) they
45756             // still might not actually intersect. This code checks that the
45757             // intersection point of the lines is actually on both line segments.
45758             var s = crossProduct(e, vb) / kross;
45759             if (s < 0 || s > 1) {
45760               // not on line segment a
45761               return null;
45762             }
45763             var t = crossProduct(e, va) / kross;
45764             if (t < 0 || t > 1) {
45765               // not on line segment b
45766               return null;
45767             }
45768             if (s === 0 || s === 1) {
45769               // on an endpoint of line segment a
45770               return noEndpointTouch ? null : [toPoint(a1, s, va)];
45771             }
45772             if (t === 0 || t === 1) {
45773               // on an endpoint of line segment b
45774               return noEndpointTouch ? null : [toPoint(b1, t, vb)];
45775             }
45776             return [toPoint(a1, s, va)];
45777           }
45778
45779           // If we've reached this point, then the lines are either parallel or the
45780           // same, but the segments could overlap partially or fully, or not at all.
45781           // So we need to find the overlap, if any. To do that, we can use e, which is
45782           // the (vector) difference between the two initial points. If this is parallel
45783           // with the line itself, then the two lines are the same line, and there will
45784           // be overlap.
45785           //const sqrLenE = dotProduct(e, e);
45786           kross = crossProduct(e, va);
45787           sqrKross = kross * kross;
45788
45789           if (sqrKross > 0 /* EPS * sqLenB * sqLenE */) {
45790           // Lines are just parallel, not the same. No overlap.
45791             return null;
45792           }
45793
45794           var sa = dotProduct(va, e) / sqrLenA;
45795           var sb = sa + dotProduct(va, vb) / sqrLenA;
45796           var smin = Math.min(sa, sb);
45797           var smax = Math.max(sa, sb);
45798
45799           // this is, essentially, the FindIntersection acting on floats from
45800           // Schneider & Eberly, just inlined into this function.
45801           if (smin <= 1 && smax >= 0) {
45802
45803             // overlap on an end point
45804             if (smin === 1) {
45805               return noEndpointTouch ? null : [toPoint(a1, smin > 0 ? smin : 0, va)];
45806             }
45807
45808             if (smax === 0) {
45809               return noEndpointTouch ? null : [toPoint(a1, smax < 1 ? smax : 1, va)];
45810             }
45811
45812             if (noEndpointTouch && smin === 0 && smax === 1) { return null; }
45813
45814             // There's overlap on a segment -- two points of intersection. Return both.
45815             return [
45816               toPoint(a1, smin > 0 ? smin : 0, va),
45817               toPoint(a1, smax < 1 ? smax : 1, va)
45818             ];
45819           }
45820
45821           return null;
45822         }
45823
45824         /**
45825          * @param  {SweepEvent} se1
45826          * @param  {SweepEvent} se2
45827          * @param  {Queue}      queue
45828          * @return {Number}
45829          */
45830         function possibleIntersection (se1, se2, queue) {
45831           // that disallows self-intersecting polygons,
45832           // did cost us half a day, so I'll leave it
45833           // out of respect
45834           // if (se1.isSubject === se2.isSubject) return;
45835           var inter = intersection(
45836             se1.point, se1.otherEvent.point,
45837             se2.point, se2.otherEvent.point
45838           );
45839
45840           var nintersections = inter ? inter.length : 0;
45841           if (nintersections === 0) { return 0; } // no intersection
45842
45843           // the line segments intersect at an endpoint of both line segments
45844           if ((nintersections === 1) &&
45845               (equals(se1.point, se2.point) ||
45846                equals(se1.otherEvent.point, se2.otherEvent.point))) {
45847             return 0;
45848           }
45849
45850           if (nintersections === 2 && se1.isSubject === se2.isSubject) {
45851             // if(se1.contourId === se2.contourId){
45852             // console.warn('Edges of the same polygon overlap',
45853             //   se1.point, se1.otherEvent.point, se2.point, se2.otherEvent.point);
45854             // }
45855             //throw new Error('Edges of the same polygon overlap');
45856             return 0;
45857           }
45858
45859           // The line segments associated to se1 and se2 intersect
45860           if (nintersections === 1) {
45861
45862             // if the intersection point is not an endpoint of se1
45863             if (!equals(se1.point, inter[0]) && !equals(se1.otherEvent.point, inter[0])) {
45864               divideSegment(se1, inter[0], queue);
45865             }
45866
45867             // if the intersection point is not an endpoint of se2
45868             if (!equals(se2.point, inter[0]) && !equals(se2.otherEvent.point, inter[0])) {
45869               divideSegment(se2, inter[0], queue);
45870             }
45871             return 1;
45872           }
45873
45874           // The line segments associated to se1 and se2 overlap
45875           var events        = [];
45876           var leftCoincide  = false;
45877           var rightCoincide = false;
45878
45879           if (equals(se1.point, se2.point)) {
45880             leftCoincide = true; // linked
45881           } else if (compareEvents(se1, se2) === 1) {
45882             events.push(se2, se1);
45883           } else {
45884             events.push(se1, se2);
45885           }
45886
45887           if (equals(se1.otherEvent.point, se2.otherEvent.point)) {
45888             rightCoincide = true;
45889           } else if (compareEvents(se1.otherEvent, se2.otherEvent) === 1) {
45890             events.push(se2.otherEvent, se1.otherEvent);
45891           } else {
45892             events.push(se1.otherEvent, se2.otherEvent);
45893           }
45894
45895           if ((leftCoincide && rightCoincide) || leftCoincide) {
45896             // both line segments are equal or share the left endpoint
45897             se2.type = NON_CONTRIBUTING;
45898             se1.type = (se2.inOut === se1.inOut)
45899               ? SAME_TRANSITION : DIFFERENT_TRANSITION;
45900
45901             if (leftCoincide && !rightCoincide) {
45902               // honestly no idea, but changing events selection from [2, 1]
45903               // to [0, 1] fixes the overlapping self-intersecting polygons issue
45904               divideSegment(events[1].otherEvent, events[0].point, queue);
45905             }
45906             return 2;
45907           }
45908
45909           // the line segments share the right endpoint
45910           if (rightCoincide) {
45911             divideSegment(events[0], events[1].point, queue);
45912             return 3;
45913           }
45914
45915           // no line segment includes totally the other one
45916           if (events[0] !== events[3].otherEvent) {
45917             divideSegment(events[0], events[1].point, queue);
45918             divideSegment(events[1], events[2].point, queue);
45919             return 3;
45920           }
45921
45922           // one line segment includes the other one
45923           divideSegment(events[0], events[1].point, queue);
45924           divideSegment(events[3].otherEvent, events[2].point, queue);
45925
45926           return 3;
45927         }
45928
45929         /**
45930          * @param  {SweepEvent} le1
45931          * @param  {SweepEvent} le2
45932          * @return {Number}
45933          */
45934         function compareSegments(le1, le2) {
45935           if (le1 === le2) { return 0; }
45936
45937           // Segments are not collinear
45938           if (signedArea(le1.point, le1.otherEvent.point, le2.point) !== 0 ||
45939             signedArea(le1.point, le1.otherEvent.point, le2.otherEvent.point) !== 0) {
45940
45941             // If they share their left endpoint use the right endpoint to sort
45942             if (equals(le1.point, le2.point)) { return le1.isBelow(le2.otherEvent.point) ? -1 : 1; }
45943
45944             // Different left endpoint: use the left endpoint to sort
45945             if (le1.point[0] === le2.point[0]) { return le1.point[1] < le2.point[1] ? -1 : 1; }
45946
45947             // has the line segment associated to e1 been inserted
45948             // into S after the line segment associated to e2 ?
45949             if (compareEvents(le1, le2) === 1) { return le2.isAbove(le1.point) ? -1 : 1; }
45950
45951             // The line segment associated to e2 has been inserted
45952             // into S after the line segment associated to e1
45953             return le1.isBelow(le2.point) ? -1 : 1;
45954           }
45955
45956           if (le1.isSubject === le2.isSubject) { // same polygon
45957             var p1 = le1.point, p2 = le2.point;
45958             if (p1[0] === p2[0] && p1[1] === p2[1]/*equals(le1.point, le2.point)*/) {
45959               p1 = le1.otherEvent.point; p2 = le2.otherEvent.point;
45960               if (p1[0] === p2[0] && p1[1] === p2[1]) { return 0; }
45961               else { return le1.contourId > le2.contourId ? 1 : -1; }
45962             }
45963           } else { // Segments are collinear, but belong to separate polygons
45964             return le1.isSubject ? -1 : 1;
45965           }
45966
45967           return compareEvents(le1, le2) === 1 ? 1 : -1;
45968         }
45969
45970         function subdivide(eventQueue, subject, clipping, sbbox, cbbox, operation) {
45971           var sweepLine = new SplayTree(compareSegments);
45972           var sortedEvents = [];
45973
45974           var rightbound = Math.min(sbbox[2], cbbox[2]);
45975
45976           var prev, next, begin;
45977
45978           while (eventQueue.length !== 0) {
45979             var event = eventQueue.pop();
45980             sortedEvents.push(event);
45981
45982             // optimization by bboxes for intersection and difference goes here
45983             if ((operation === INTERSECTION && event.point[0] > rightbound) ||
45984                 (operation === DIFFERENCE   && event.point[0] > sbbox[2])) {
45985               break;
45986             }
45987
45988             if (event.left) {
45989               next  = prev = sweepLine.insert(event);
45990               begin = sweepLine.minNode();
45991
45992               if (prev !== begin) { prev = sweepLine.prev(prev); }
45993               else                { prev = null; }
45994
45995               next = sweepLine.next(next);
45996
45997               var prevEvent = prev ? prev.key : null;
45998               var prevprevEvent = (void 0);
45999               computeFields(event, prevEvent, operation);
46000               if (next) {
46001                 if (possibleIntersection(event, next.key, eventQueue) === 2) {
46002                   computeFields(event, prevEvent, operation);
46003                   computeFields(event, next.key, operation);
46004                 }
46005               }
46006
46007               if (prev) {
46008                 if (possibleIntersection(prev.key, event, eventQueue) === 2) {
46009                   var prevprev = prev;
46010                   if (prevprev !== begin) { prevprev = sweepLine.prev(prevprev); }
46011                   else                    { prevprev = null; }
46012
46013                   prevprevEvent = prevprev ? prevprev.key : null;
46014                   computeFields(prevEvent, prevprevEvent, operation);
46015                   computeFields(event,     prevEvent,     operation);
46016                 }
46017               }
46018             } else {
46019               event = event.otherEvent;
46020               next = prev = sweepLine.find(event);
46021
46022               if (prev && next) {
46023
46024                 if (prev !== begin) { prev = sweepLine.prev(prev); }
46025                 else                { prev = null; }
46026
46027                 next = sweepLine.next(next);
46028                 sweepLine.remove(event);
46029
46030                 if (next && prev) {
46031                   possibleIntersection(prev.key, next.key, eventQueue);
46032                 }
46033               }
46034             }
46035           }
46036           return sortedEvents;
46037         }
46038
46039         var Contour = function Contour() {
46040           this.points = [];
46041           this.holeIds = [];
46042           this.holeOf = null;
46043           this.depth = null;
46044         };
46045
46046         Contour.prototype.isExterior = function isExterior () {
46047           return this.holeOf == null;
46048         };
46049
46050         /**
46051          * @param  {Array.<SweepEvent>} sortedEvents
46052          * @return {Array.<SweepEvent>}
46053          */
46054         function orderEvents(sortedEvents) {
46055           var event, i, len, tmp;
46056           var resultEvents = [];
46057           for (i = 0, len = sortedEvents.length; i < len; i++) {
46058             event = sortedEvents[i];
46059             if ((event.left && event.inResult) ||
46060               (!event.left && event.otherEvent.inResult)) {
46061               resultEvents.push(event);
46062             }
46063           }
46064           // Due to overlapping edges the resultEvents array can be not wholly sorted
46065           var sorted = false;
46066           while (!sorted) {
46067             sorted = true;
46068             for (i = 0, len = resultEvents.length; i < len; i++) {
46069               if ((i + 1) < len &&
46070                 compareEvents(resultEvents[i], resultEvents[i + 1]) === 1) {
46071                 tmp = resultEvents[i];
46072                 resultEvents[i] = resultEvents[i + 1];
46073                 resultEvents[i + 1] = tmp;
46074                 sorted = false;
46075               }
46076             }
46077           }
46078
46079
46080           for (i = 0, len = resultEvents.length; i < len; i++) {
46081             event = resultEvents[i];
46082             event.otherPos = i;
46083           }
46084
46085           // imagine, the right event is found in the beginning of the queue,
46086           // when his left counterpart is not marked yet
46087           for (i = 0, len = resultEvents.length; i < len; i++) {
46088             event = resultEvents[i];
46089             if (!event.left) {
46090               tmp = event.otherPos;
46091               event.otherPos = event.otherEvent.otherPos;
46092               event.otherEvent.otherPos = tmp;
46093             }
46094           }
46095
46096           return resultEvents;
46097         }
46098
46099
46100         /**
46101          * @param  {Number} pos
46102          * @param  {Array.<SweepEvent>} resultEvents
46103          * @param  {Object>}    processed
46104          * @return {Number}
46105          */
46106         function nextPos(pos, resultEvents, processed, origPos) {
46107           var newPos = pos + 1,
46108               p = resultEvents[pos].point,
46109               p1;
46110           var length = resultEvents.length;
46111
46112           if (newPos < length)
46113             { p1 = resultEvents[newPos].point; }
46114
46115           while (newPos < length && p1[0] === p[0] && p1[1] === p[1]) {
46116             if (!processed[newPos]) {
46117               return newPos;
46118             } else   {
46119               newPos++;
46120             }
46121             p1 = resultEvents[newPos].point;
46122           }
46123
46124           newPos = pos - 1;
46125
46126           while (processed[newPos] && newPos > origPos) {
46127             newPos--;
46128           }
46129
46130           return newPos;
46131         }
46132
46133
46134         function initializeContourFromContext(event, contours, contourId) {
46135           var contour = new Contour();
46136           if (event.prevInResult != null) {
46137             var prevInResult = event.prevInResult;
46138             // Note that it is valid to query the "previous in result" for its output contour id,
46139             // because we must have already processed it (i.e., assigned an output contour id)
46140             // in an earlier iteration, otherwise it wouldn't be possible that it is "previous in
46141             // result".
46142             var lowerContourId = prevInResult.outputContourId;
46143             var lowerResultTransition = prevInResult.resultTransition;
46144             if (lowerResultTransition > 0) {
46145               // We are inside. Now we have to check if the thing below us is another hole or
46146               // an exterior contour.
46147               var lowerContour = contours[lowerContourId];
46148               if (lowerContour.holeOf != null) {
46149                 // The lower contour is a hole => Connect the new contour as a hole to its parent,
46150                 // and use same depth.
46151                 var parentContourId = lowerContour.holeOf;
46152                 contours[parentContourId].holeIds.push(contourId);
46153                 contour.holeOf = parentContourId;
46154                 contour.depth = contours[lowerContourId].depth;
46155               } else {
46156                 // The lower contour is an exterior contour => Connect the new contour as a hole,
46157                 // and increment depth.
46158                 contours[lowerContourId].holeIds.push(contourId);
46159                 contour.holeOf = lowerContourId;
46160                 contour.depth = contours[lowerContourId].depth + 1;
46161               }
46162             } else {
46163               // We are outside => this contour is an exterior contour of same depth.
46164               contour.holeOf = null;
46165               contour.depth = contours[lowerContourId].depth;
46166             }
46167           } else {
46168             // There is no lower/previous contour => this contour is an exterior contour of depth 0.
46169             contour.holeOf = null;
46170             contour.depth = 0;
46171           }
46172           return contour;
46173         }
46174
46175         /**
46176          * @param  {Array.<SweepEvent>} sortedEvents
46177          * @return {Array.<*>} polygons
46178          */
46179         function connectEdges(sortedEvents) {
46180           var i, len;
46181           var resultEvents = orderEvents(sortedEvents);
46182
46183           // "false"-filled array
46184           var processed = {};
46185           var contours = [];
46186
46187           var loop = function (  ) {
46188
46189             if (processed[i]) {
46190               return;
46191             }
46192
46193             var contourId = contours.length;
46194             var contour = initializeContourFromContext(resultEvents[i], contours, contourId);
46195
46196             // Helper function that combines marking an event as processed with assigning its output contour ID
46197             var markAsProcessed = function (pos) {
46198               processed[pos] = true;
46199               resultEvents[pos].outputContourId = contourId;
46200             };
46201
46202             var pos = i;
46203             var origPos = i;
46204
46205             var initial = resultEvents[i].point;
46206             contour.points.push(initial);
46207
46208             /* eslint no-constant-condition: "off" */
46209             while (true) {
46210               markAsProcessed(pos);
46211
46212               pos = resultEvents[pos].otherPos;
46213
46214               markAsProcessed(pos);
46215               contour.points.push(resultEvents[pos].point);
46216
46217               pos = nextPos(pos, resultEvents, processed, origPos);
46218
46219               if (pos == origPos) {
46220                 break;
46221               }
46222             }
46223
46224             contours.push(contour);
46225           };
46226
46227           for (i = 0, len = resultEvents.length; i < len; i++) loop(  );
46228
46229           return contours;
46230         }
46231
46232         var tinyqueue = TinyQueue;
46233         var _default$1 = TinyQueue;
46234
46235         function TinyQueue(data, compare) {
46236             if (!(this instanceof TinyQueue)) { return new TinyQueue(data, compare); }
46237
46238             this.data = data || [];
46239             this.length = this.data.length;
46240             this.compare = compare || defaultCompare$1;
46241
46242             if (this.length > 0) {
46243                 for (var i = (this.length >> 1) - 1; i >= 0; i--) { this._down(i); }
46244             }
46245         }
46246
46247         function defaultCompare$1(a, b) {
46248             return a < b ? -1 : a > b ? 1 : 0;
46249         }
46250
46251         TinyQueue.prototype = {
46252
46253             push: function (item) {
46254                 this.data.push(item);
46255                 this.length++;
46256                 this._up(this.length - 1);
46257             },
46258
46259             pop: function () {
46260                 if (this.length === 0) { return undefined; }
46261
46262                 var top = this.data[0];
46263                 this.length--;
46264
46265                 if (this.length > 0) {
46266                     this.data[0] = this.data[this.length];
46267                     this._down(0);
46268                 }
46269                 this.data.pop();
46270
46271                 return top;
46272             },
46273
46274             peek: function () {
46275                 return this.data[0];
46276             },
46277
46278             _up: function (pos) {
46279                 var data = this.data;
46280                 var compare = this.compare;
46281                 var item = data[pos];
46282
46283                 while (pos > 0) {
46284                     var parent = (pos - 1) >> 1;
46285                     var current = data[parent];
46286                     if (compare(item, current) >= 0) { break; }
46287                     data[pos] = current;
46288                     pos = parent;
46289                 }
46290
46291                 data[pos] = item;
46292             },
46293
46294             _down: function (pos) {
46295                 var data = this.data;
46296                 var compare = this.compare;
46297                 var halfLength = this.length >> 1;
46298                 var item = data[pos];
46299
46300                 while (pos < halfLength) {
46301                     var left = (pos << 1) + 1;
46302                     var right = left + 1;
46303                     var best = data[left];
46304
46305                     if (right < this.length && compare(data[right], best) < 0) {
46306                         left = right;
46307                         best = data[right];
46308                     }
46309                     if (compare(best, item) >= 0) { break; }
46310
46311                     data[pos] = best;
46312                     pos = left;
46313                 }
46314
46315                 data[pos] = item;
46316             }
46317         };
46318         tinyqueue.default = _default$1;
46319
46320         var max$2 = Math.max;
46321         var min = Math.min;
46322
46323         var contourId = 0;
46324
46325
46326         function processPolygon(contourOrHole, isSubject, depth, Q, bbox, isExteriorRing) {
46327           var i, len, s1, s2, e1, e2;
46328           for (i = 0, len = contourOrHole.length - 1; i < len; i++) {
46329             s1 = contourOrHole[i];
46330             s2 = contourOrHole[i + 1];
46331             e1 = new SweepEvent(s1, false, undefined, isSubject);
46332             e2 = new SweepEvent(s2, false, e1,        isSubject);
46333             e1.otherEvent = e2;
46334
46335             if (s1[0] === s2[0] && s1[1] === s2[1]) {
46336               continue; // skip collapsed edges, or it breaks
46337             }
46338
46339             e1.contourId = e2.contourId = depth;
46340             if (!isExteriorRing) {
46341               e1.isExteriorRing = false;
46342               e2.isExteriorRing = false;
46343             }
46344             if (compareEvents(e1, e2) > 0) {
46345               e2.left = true;
46346             } else {
46347               e1.left = true;
46348             }
46349
46350             var x = s1[0], y = s1[1];
46351             bbox[0] = min(bbox[0], x);
46352             bbox[1] = min(bbox[1], y);
46353             bbox[2] = max$2(bbox[2], x);
46354             bbox[3] = max$2(bbox[3], y);
46355
46356             // Pushing it so the queue is sorted from left to right,
46357             // with object on the left having the highest priority.
46358             Q.push(e1);
46359             Q.push(e2);
46360           }
46361         }
46362
46363
46364         function fillQueue(subject, clipping, sbbox, cbbox, operation) {
46365           var eventQueue = new tinyqueue(null, compareEvents);
46366           var polygonSet, isExteriorRing, i, ii, j, jj; //, k, kk;
46367
46368           for (i = 0, ii = subject.length; i < ii; i++) {
46369             polygonSet = subject[i];
46370             for (j = 0, jj = polygonSet.length; j < jj; j++) {
46371               isExteriorRing = j === 0;
46372               if (isExteriorRing) { contourId++; }
46373               processPolygon(polygonSet[j], true, contourId, eventQueue, sbbox, isExteriorRing);
46374             }
46375           }
46376
46377           for (i = 0, ii = clipping.length; i < ii; i++) {
46378             polygonSet = clipping[i];
46379             for (j = 0, jj = polygonSet.length; j < jj; j++) {
46380               isExteriorRing = j === 0;
46381               if (operation === DIFFERENCE) { isExteriorRing = false; }
46382               if (isExteriorRing) { contourId++; }
46383               processPolygon(polygonSet[j], false, contourId, eventQueue, cbbox, isExteriorRing);
46384             }
46385           }
46386
46387           return eventQueue;
46388         }
46389
46390         var EMPTY = [];
46391
46392
46393         function trivialOperation(subject, clipping, operation) {
46394           var result = null;
46395           if (subject.length * clipping.length === 0) {
46396             if        (operation === INTERSECTION) {
46397               result = EMPTY;
46398             } else if (operation === DIFFERENCE) {
46399               result = subject;
46400             } else if (operation === UNION ||
46401                        operation === XOR) {
46402               result = (subject.length === 0) ? clipping : subject;
46403             }
46404           }
46405           return result;
46406         }
46407
46408
46409         function compareBBoxes(subject, clipping, sbbox, cbbox, operation) {
46410           var result = null;
46411           if (sbbox[0] > cbbox[2] ||
46412               cbbox[0] > sbbox[2] ||
46413               sbbox[1] > cbbox[3] ||
46414               cbbox[1] > sbbox[3]) {
46415             if        (operation === INTERSECTION) {
46416               result = EMPTY;
46417             } else if (operation === DIFFERENCE) {
46418               result = subject;
46419             } else if (operation === UNION ||
46420                        operation === XOR) {
46421               result = subject.concat(clipping);
46422             }
46423           }
46424           return result;
46425         }
46426
46427
46428         function boolean(subject, clipping, operation) {
46429           if (typeof subject[0][0][0] === 'number') {
46430             subject = [subject];
46431           }
46432           if (typeof clipping[0][0][0] === 'number') {
46433             clipping = [clipping];
46434           }
46435           var trivial = trivialOperation(subject, clipping, operation);
46436           if (trivial) {
46437             return trivial === EMPTY ? null : trivial;
46438           }
46439           var sbbox = [Infinity, Infinity, -Infinity, -Infinity];
46440           var cbbox = [Infinity, Infinity, -Infinity, -Infinity];
46441
46442           // console.time('fill queue');
46443           var eventQueue = fillQueue(subject, clipping, sbbox, cbbox, operation);
46444           //console.timeEnd('fill queue');
46445
46446           trivial = compareBBoxes(subject, clipping, sbbox, cbbox, operation);
46447           if (trivial) {
46448             return trivial === EMPTY ? null : trivial;
46449           }
46450           // console.time('subdivide edges');
46451           var sortedEvents = subdivide(eventQueue, subject, clipping, sbbox, cbbox, operation);
46452           //console.timeEnd('subdivide edges');
46453
46454           // console.time('connect vertices');
46455           var contours = connectEdges(sortedEvents);
46456           //console.timeEnd('connect vertices');
46457
46458           // Convert contours to polygons
46459           var polygons = [];
46460           for (var i = 0; i < contours.length; i++) {
46461             var contour = contours[i];
46462             if (contour.isExterior()) {
46463               // The exterior ring goes first
46464               var rings = [contour.points];
46465               // Followed by holes if any
46466               for (var j = 0; j < contour.holeIds.length; j++) {
46467                 var holeId = contour.holeIds[j];
46468                 rings.push(contours[holeId].points);
46469               }
46470               polygons.push(rings);
46471             }
46472           }
46473
46474           return polygons;
46475         }
46476
46477         function union (subject, clipping) {
46478           return boolean(subject, clipping, UNION);
46479         }
46480
46481         var read$6 = function (buffer, offset, isLE, mLen, nBytes) {
46482           var e, m;
46483           var eLen = (nBytes * 8) - mLen - 1;
46484           var eMax = (1 << eLen) - 1;
46485           var eBias = eMax >> 1;
46486           var nBits = -7;
46487           var i = isLE ? (nBytes - 1) : 0;
46488           var d = isLE ? -1 : 1;
46489           var s = buffer[offset + i];
46490
46491           i += d;
46492
46493           e = s & ((1 << (-nBits)) - 1);
46494           s >>= (-nBits);
46495           nBits += eLen;
46496           for (; nBits > 0; e = (e * 256) + buffer[offset + i], i += d, nBits -= 8) {}
46497
46498           m = e & ((1 << (-nBits)) - 1);
46499           e >>= (-nBits);
46500           nBits += mLen;
46501           for (; nBits > 0; m = (m * 256) + buffer[offset + i], i += d, nBits -= 8) {}
46502
46503           if (e === 0) {
46504             e = 1 - eBias;
46505           } else if (e === eMax) {
46506             return m ? NaN : ((s ? -1 : 1) * Infinity)
46507           } else {
46508             m = m + Math.pow(2, mLen);
46509             e = e - eBias;
46510           }
46511           return (s ? -1 : 1) * m * Math.pow(2, e - mLen)
46512         };
46513
46514         var write$6 = function (buffer, value, offset, isLE, mLen, nBytes) {
46515           var e, m, c;
46516           var eLen = (nBytes * 8) - mLen - 1;
46517           var eMax = (1 << eLen) - 1;
46518           var eBias = eMax >> 1;
46519           var rt = (mLen === 23 ? Math.pow(2, -24) - Math.pow(2, -77) : 0);
46520           var i = isLE ? 0 : (nBytes - 1);
46521           var d = isLE ? 1 : -1;
46522           var s = value < 0 || (value === 0 && 1 / value < 0) ? 1 : 0;
46523
46524           value = Math.abs(value);
46525
46526           if (isNaN(value) || value === Infinity) {
46527             m = isNaN(value) ? 1 : 0;
46528             e = eMax;
46529           } else {
46530             e = Math.floor(Math.log(value) / Math.LN2);
46531             if (value * (c = Math.pow(2, -e)) < 1) {
46532               e--;
46533               c *= 2;
46534             }
46535             if (e + eBias >= 1) {
46536               value += rt / c;
46537             } else {
46538               value += rt * Math.pow(2, 1 - eBias);
46539             }
46540             if (value * c >= 2) {
46541               e++;
46542               c /= 2;
46543             }
46544
46545             if (e + eBias >= eMax) {
46546               m = 0;
46547               e = eMax;
46548             } else if (e + eBias >= 1) {
46549               m = ((value * c) - 1) * Math.pow(2, mLen);
46550               e = e + eBias;
46551             } else {
46552               m = value * Math.pow(2, eBias - 1) * Math.pow(2, mLen);
46553               e = 0;
46554             }
46555           }
46556
46557           for (; mLen >= 8; buffer[offset + i] = m & 0xff, i += d, m /= 256, mLen -= 8) {}
46558
46559           e = (e << mLen) | m;
46560           eLen += mLen;
46561           for (; eLen > 0; buffer[offset + i] = e & 0xff, i += d, e /= 256, eLen -= 8) {}
46562
46563           buffer[offset + i - d] |= s * 128;
46564         };
46565
46566         var ieee754 = {
46567                 read: read$6,
46568                 write: write$6
46569         };
46570
46571         var pbf = Pbf;
46572
46573
46574
46575         function Pbf(buf) {
46576             this.buf = ArrayBuffer.isView && ArrayBuffer.isView(buf) ? buf : new Uint8Array(buf || 0);
46577             this.pos = 0;
46578             this.type = 0;
46579             this.length = this.buf.length;
46580         }
46581
46582         Pbf.Varint  = 0; // varint: int32, int64, uint32, uint64, sint32, sint64, bool, enum
46583         Pbf.Fixed64 = 1; // 64-bit: double, fixed64, sfixed64
46584         Pbf.Bytes   = 2; // length-delimited: string, bytes, embedded messages, packed repeated fields
46585         Pbf.Fixed32 = 5; // 32-bit: float, fixed32, sfixed32
46586
46587         var SHIFT_LEFT_32 = (1 << 16) * (1 << 16),
46588             SHIFT_RIGHT_32 = 1 / SHIFT_LEFT_32;
46589
46590         // Threshold chosen based on both benchmarking and knowledge about browser string
46591         // data structures (which currently switch structure types at 12 bytes or more)
46592         var TEXT_DECODER_MIN_LENGTH = 12;
46593         var utf8TextDecoder = typeof TextDecoder === 'undefined' ? null : new TextDecoder('utf8');
46594
46595         Pbf.prototype = {
46596
46597             destroy: function() {
46598                 this.buf = null;
46599             },
46600
46601             // === READING =================================================================
46602
46603             readFields: function(readField, result, end) {
46604                 end = end || this.length;
46605
46606                 while (this.pos < end) {
46607                     var val = this.readVarint(),
46608                         tag = val >> 3,
46609                         startPos = this.pos;
46610
46611                     this.type = val & 0x7;
46612                     readField(tag, result, this);
46613
46614                     if (this.pos === startPos) { this.skip(val); }
46615                 }
46616                 return result;
46617             },
46618
46619             readMessage: function(readField, result) {
46620                 return this.readFields(readField, result, this.readVarint() + this.pos);
46621             },
46622
46623             readFixed32: function() {
46624                 var val = readUInt32(this.buf, this.pos);
46625                 this.pos += 4;
46626                 return val;
46627             },
46628
46629             readSFixed32: function() {
46630                 var val = readInt32(this.buf, this.pos);
46631                 this.pos += 4;
46632                 return val;
46633             },
46634
46635             // 64-bit int handling is based on github.com/dpw/node-buffer-more-ints (MIT-licensed)
46636
46637             readFixed64: function() {
46638                 var val = readUInt32(this.buf, this.pos) + readUInt32(this.buf, this.pos + 4) * SHIFT_LEFT_32;
46639                 this.pos += 8;
46640                 return val;
46641             },
46642
46643             readSFixed64: function() {
46644                 var val = readUInt32(this.buf, this.pos) + readInt32(this.buf, this.pos + 4) * SHIFT_LEFT_32;
46645                 this.pos += 8;
46646                 return val;
46647             },
46648
46649             readFloat: function() {
46650                 var val = ieee754.read(this.buf, this.pos, true, 23, 4);
46651                 this.pos += 4;
46652                 return val;
46653             },
46654
46655             readDouble: function() {
46656                 var val = ieee754.read(this.buf, this.pos, true, 52, 8);
46657                 this.pos += 8;
46658                 return val;
46659             },
46660
46661             readVarint: function(isSigned) {
46662                 var buf = this.buf,
46663                     val, b;
46664
46665                 b = buf[this.pos++]; val  =  b & 0x7f;        if (b < 0x80) { return val; }
46666                 b = buf[this.pos++]; val |= (b & 0x7f) << 7;  if (b < 0x80) { return val; }
46667                 b = buf[this.pos++]; val |= (b & 0x7f) << 14; if (b < 0x80) { return val; }
46668                 b = buf[this.pos++]; val |= (b & 0x7f) << 21; if (b < 0x80) { return val; }
46669                 b = buf[this.pos];   val |= (b & 0x0f) << 28;
46670
46671                 return readVarintRemainder(val, isSigned, this);
46672             },
46673
46674             readVarint64: function() { // for compatibility with v2.0.1
46675                 return this.readVarint(true);
46676             },
46677
46678             readSVarint: function() {
46679                 var num = this.readVarint();
46680                 return num % 2 === 1 ? (num + 1) / -2 : num / 2; // zigzag encoding
46681             },
46682
46683             readBoolean: function() {
46684                 return Boolean(this.readVarint());
46685             },
46686
46687             readString: function() {
46688                 var end = this.readVarint() + this.pos;
46689                 var pos = this.pos;
46690                 this.pos = end;
46691
46692                 if (end - pos >= TEXT_DECODER_MIN_LENGTH && utf8TextDecoder) {
46693                     // longer strings are fast with the built-in browser TextDecoder API
46694                     return readUtf8TextDecoder(this.buf, pos, end);
46695                 }
46696                 // short strings are fast with our custom implementation
46697                 return readUtf8(this.buf, pos, end);
46698             },
46699
46700             readBytes: function() {
46701                 var end = this.readVarint() + this.pos,
46702                     buffer = this.buf.subarray(this.pos, end);
46703                 this.pos = end;
46704                 return buffer;
46705             },
46706
46707             // verbose for performance reasons; doesn't affect gzipped size
46708
46709             readPackedVarint: function(arr, isSigned) {
46710                 if (this.type !== Pbf.Bytes) { return arr.push(this.readVarint(isSigned)); }
46711                 var end = readPackedEnd(this);
46712                 arr = arr || [];
46713                 while (this.pos < end) { arr.push(this.readVarint(isSigned)); }
46714                 return arr;
46715             },
46716             readPackedSVarint: function(arr) {
46717                 if (this.type !== Pbf.Bytes) { return arr.push(this.readSVarint()); }
46718                 var end = readPackedEnd(this);
46719                 arr = arr || [];
46720                 while (this.pos < end) { arr.push(this.readSVarint()); }
46721                 return arr;
46722             },
46723             readPackedBoolean: function(arr) {
46724                 if (this.type !== Pbf.Bytes) { return arr.push(this.readBoolean()); }
46725                 var end = readPackedEnd(this);
46726                 arr = arr || [];
46727                 while (this.pos < end) { arr.push(this.readBoolean()); }
46728                 return arr;
46729             },
46730             readPackedFloat: function(arr) {
46731                 if (this.type !== Pbf.Bytes) { return arr.push(this.readFloat()); }
46732                 var end = readPackedEnd(this);
46733                 arr = arr || [];
46734                 while (this.pos < end) { arr.push(this.readFloat()); }
46735                 return arr;
46736             },
46737             readPackedDouble: function(arr) {
46738                 if (this.type !== Pbf.Bytes) { return arr.push(this.readDouble()); }
46739                 var end = readPackedEnd(this);
46740                 arr = arr || [];
46741                 while (this.pos < end) { arr.push(this.readDouble()); }
46742                 return arr;
46743             },
46744             readPackedFixed32: function(arr) {
46745                 if (this.type !== Pbf.Bytes) { return arr.push(this.readFixed32()); }
46746                 var end = readPackedEnd(this);
46747                 arr = arr || [];
46748                 while (this.pos < end) { arr.push(this.readFixed32()); }
46749                 return arr;
46750             },
46751             readPackedSFixed32: function(arr) {
46752                 if (this.type !== Pbf.Bytes) { return arr.push(this.readSFixed32()); }
46753                 var end = readPackedEnd(this);
46754                 arr = arr || [];
46755                 while (this.pos < end) { arr.push(this.readSFixed32()); }
46756                 return arr;
46757             },
46758             readPackedFixed64: function(arr) {
46759                 if (this.type !== Pbf.Bytes) { return arr.push(this.readFixed64()); }
46760                 var end = readPackedEnd(this);
46761                 arr = arr || [];
46762                 while (this.pos < end) { arr.push(this.readFixed64()); }
46763                 return arr;
46764             },
46765             readPackedSFixed64: function(arr) {
46766                 if (this.type !== Pbf.Bytes) { return arr.push(this.readSFixed64()); }
46767                 var end = readPackedEnd(this);
46768                 arr = arr || [];
46769                 while (this.pos < end) { arr.push(this.readSFixed64()); }
46770                 return arr;
46771             },
46772
46773             skip: function(val) {
46774                 var type = val & 0x7;
46775                 if (type === Pbf.Varint) { while (this.buf[this.pos++] > 0x7f) {} }
46776                 else if (type === Pbf.Bytes) { this.pos = this.readVarint() + this.pos; }
46777                 else if (type === Pbf.Fixed32) { this.pos += 4; }
46778                 else if (type === Pbf.Fixed64) { this.pos += 8; }
46779                 else { throw new Error('Unimplemented type: ' + type); }
46780             },
46781
46782             // === WRITING =================================================================
46783
46784             writeTag: function(tag, type) {
46785                 this.writeVarint((tag << 3) | type);
46786             },
46787
46788             realloc: function(min) {
46789                 var length = this.length || 16;
46790
46791                 while (length < this.pos + min) { length *= 2; }
46792
46793                 if (length !== this.length) {
46794                     var buf = new Uint8Array(length);
46795                     buf.set(this.buf);
46796                     this.buf = buf;
46797                     this.length = length;
46798                 }
46799             },
46800
46801             finish: function() {
46802                 this.length = this.pos;
46803                 this.pos = 0;
46804                 return this.buf.subarray(0, this.length);
46805             },
46806
46807             writeFixed32: function(val) {
46808                 this.realloc(4);
46809                 writeInt32(this.buf, val, this.pos);
46810                 this.pos += 4;
46811             },
46812
46813             writeSFixed32: function(val) {
46814                 this.realloc(4);
46815                 writeInt32(this.buf, val, this.pos);
46816                 this.pos += 4;
46817             },
46818
46819             writeFixed64: function(val) {
46820                 this.realloc(8);
46821                 writeInt32(this.buf, val & -1, this.pos);
46822                 writeInt32(this.buf, Math.floor(val * SHIFT_RIGHT_32), this.pos + 4);
46823                 this.pos += 8;
46824             },
46825
46826             writeSFixed64: function(val) {
46827                 this.realloc(8);
46828                 writeInt32(this.buf, val & -1, this.pos);
46829                 writeInt32(this.buf, Math.floor(val * SHIFT_RIGHT_32), this.pos + 4);
46830                 this.pos += 8;
46831             },
46832
46833             writeVarint: function(val) {
46834                 val = +val || 0;
46835
46836                 if (val > 0xfffffff || val < 0) {
46837                     writeBigVarint(val, this);
46838                     return;
46839                 }
46840
46841                 this.realloc(4);
46842
46843                 this.buf[this.pos++] =           val & 0x7f  | (val > 0x7f ? 0x80 : 0); if (val <= 0x7f) { return; }
46844                 this.buf[this.pos++] = ((val >>>= 7) & 0x7f) | (val > 0x7f ? 0x80 : 0); if (val <= 0x7f) { return; }
46845                 this.buf[this.pos++] = ((val >>>= 7) & 0x7f) | (val > 0x7f ? 0x80 : 0); if (val <= 0x7f) { return; }
46846                 this.buf[this.pos++] =   (val >>> 7) & 0x7f;
46847             },
46848
46849             writeSVarint: function(val) {
46850                 this.writeVarint(val < 0 ? -val * 2 - 1 : val * 2);
46851             },
46852
46853             writeBoolean: function(val) {
46854                 this.writeVarint(Boolean(val));
46855             },
46856
46857             writeString: function(str) {
46858                 str = String(str);
46859                 this.realloc(str.length * 4);
46860
46861                 this.pos++; // reserve 1 byte for short string length
46862
46863                 var startPos = this.pos;
46864                 // write the string directly to the buffer and see how much was written
46865                 this.pos = writeUtf8(this.buf, str, this.pos);
46866                 var len = this.pos - startPos;
46867
46868                 if (len >= 0x80) { makeRoomForExtraLength(startPos, len, this); }
46869
46870                 // finally, write the message length in the reserved place and restore the position
46871                 this.pos = startPos - 1;
46872                 this.writeVarint(len);
46873                 this.pos += len;
46874             },
46875
46876             writeFloat: function(val) {
46877                 this.realloc(4);
46878                 ieee754.write(this.buf, val, this.pos, true, 23, 4);
46879                 this.pos += 4;
46880             },
46881
46882             writeDouble: function(val) {
46883                 this.realloc(8);
46884                 ieee754.write(this.buf, val, this.pos, true, 52, 8);
46885                 this.pos += 8;
46886             },
46887
46888             writeBytes: function(buffer) {
46889                 var len = buffer.length;
46890                 this.writeVarint(len);
46891                 this.realloc(len);
46892                 for (var i = 0; i < len; i++) { this.buf[this.pos++] = buffer[i]; }
46893             },
46894
46895             writeRawMessage: function(fn, obj) {
46896                 this.pos++; // reserve 1 byte for short message length
46897
46898                 // write the message directly to the buffer and see how much was written
46899                 var startPos = this.pos;
46900                 fn(obj, this);
46901                 var len = this.pos - startPos;
46902
46903                 if (len >= 0x80) { makeRoomForExtraLength(startPos, len, this); }
46904
46905                 // finally, write the message length in the reserved place and restore the position
46906                 this.pos = startPos - 1;
46907                 this.writeVarint(len);
46908                 this.pos += len;
46909             },
46910
46911             writeMessage: function(tag, fn, obj) {
46912                 this.writeTag(tag, Pbf.Bytes);
46913                 this.writeRawMessage(fn, obj);
46914             },
46915
46916             writePackedVarint:   function(tag, arr) { if (arr.length) { this.writeMessage(tag, writePackedVarint, arr); }   },
46917             writePackedSVarint:  function(tag, arr) { if (arr.length) { this.writeMessage(tag, writePackedSVarint, arr); }  },
46918             writePackedBoolean:  function(tag, arr) { if (arr.length) { this.writeMessage(tag, writePackedBoolean, arr); }  },
46919             writePackedFloat:    function(tag, arr) { if (arr.length) { this.writeMessage(tag, writePackedFloat, arr); }    },
46920             writePackedDouble:   function(tag, arr) { if (arr.length) { this.writeMessage(tag, writePackedDouble, arr); }   },
46921             writePackedFixed32:  function(tag, arr) { if (arr.length) { this.writeMessage(tag, writePackedFixed32, arr); }  },
46922             writePackedSFixed32: function(tag, arr) { if (arr.length) { this.writeMessage(tag, writePackedSFixed32, arr); } },
46923             writePackedFixed64:  function(tag, arr) { if (arr.length) { this.writeMessage(tag, writePackedFixed64, arr); }  },
46924             writePackedSFixed64: function(tag, arr) { if (arr.length) { this.writeMessage(tag, writePackedSFixed64, arr); } },
46925
46926             writeBytesField: function(tag, buffer) {
46927                 this.writeTag(tag, Pbf.Bytes);
46928                 this.writeBytes(buffer);
46929             },
46930             writeFixed32Field: function(tag, val) {
46931                 this.writeTag(tag, Pbf.Fixed32);
46932                 this.writeFixed32(val);
46933             },
46934             writeSFixed32Field: function(tag, val) {
46935                 this.writeTag(tag, Pbf.Fixed32);
46936                 this.writeSFixed32(val);
46937             },
46938             writeFixed64Field: function(tag, val) {
46939                 this.writeTag(tag, Pbf.Fixed64);
46940                 this.writeFixed64(val);
46941             },
46942             writeSFixed64Field: function(tag, val) {
46943                 this.writeTag(tag, Pbf.Fixed64);
46944                 this.writeSFixed64(val);
46945             },
46946             writeVarintField: function(tag, val) {
46947                 this.writeTag(tag, Pbf.Varint);
46948                 this.writeVarint(val);
46949             },
46950             writeSVarintField: function(tag, val) {
46951                 this.writeTag(tag, Pbf.Varint);
46952                 this.writeSVarint(val);
46953             },
46954             writeStringField: function(tag, str) {
46955                 this.writeTag(tag, Pbf.Bytes);
46956                 this.writeString(str);
46957             },
46958             writeFloatField: function(tag, val) {
46959                 this.writeTag(tag, Pbf.Fixed32);
46960                 this.writeFloat(val);
46961             },
46962             writeDoubleField: function(tag, val) {
46963                 this.writeTag(tag, Pbf.Fixed64);
46964                 this.writeDouble(val);
46965             },
46966             writeBooleanField: function(tag, val) {
46967                 this.writeVarintField(tag, Boolean(val));
46968             }
46969         };
46970
46971         function readVarintRemainder(l, s, p) {
46972             var buf = p.buf,
46973                 h, b;
46974
46975             b = buf[p.pos++]; h  = (b & 0x70) >> 4;  if (b < 0x80) { return toNum(l, h, s); }
46976             b = buf[p.pos++]; h |= (b & 0x7f) << 3;  if (b < 0x80) { return toNum(l, h, s); }
46977             b = buf[p.pos++]; h |= (b & 0x7f) << 10; if (b < 0x80) { return toNum(l, h, s); }
46978             b = buf[p.pos++]; h |= (b & 0x7f) << 17; if (b < 0x80) { return toNum(l, h, s); }
46979             b = buf[p.pos++]; h |= (b & 0x7f) << 24; if (b < 0x80) { return toNum(l, h, s); }
46980             b = buf[p.pos++]; h |= (b & 0x01) << 31; if (b < 0x80) { return toNum(l, h, s); }
46981
46982             throw new Error('Expected varint not more than 10 bytes');
46983         }
46984
46985         function readPackedEnd(pbf) {
46986             return pbf.type === Pbf.Bytes ?
46987                 pbf.readVarint() + pbf.pos : pbf.pos + 1;
46988         }
46989
46990         function toNum(low, high, isSigned) {
46991             if (isSigned) {
46992                 return high * 0x100000000 + (low >>> 0);
46993             }
46994
46995             return ((high >>> 0) * 0x100000000) + (low >>> 0);
46996         }
46997
46998         function writeBigVarint(val, pbf) {
46999             var low, high;
47000
47001             if (val >= 0) {
47002                 low  = (val % 0x100000000) | 0;
47003                 high = (val / 0x100000000) | 0;
47004             } else {
47005                 low  = ~(-val % 0x100000000);
47006                 high = ~(-val / 0x100000000);
47007
47008                 if (low ^ 0xffffffff) {
47009                     low = (low + 1) | 0;
47010                 } else {
47011                     low = 0;
47012                     high = (high + 1) | 0;
47013                 }
47014             }
47015
47016             if (val >= 0x10000000000000000 || val < -0x10000000000000000) {
47017                 throw new Error('Given varint doesn\'t fit into 10 bytes');
47018             }
47019
47020             pbf.realloc(10);
47021
47022             writeBigVarintLow(low, high, pbf);
47023             writeBigVarintHigh(high, pbf);
47024         }
47025
47026         function writeBigVarintLow(low, high, pbf) {
47027             pbf.buf[pbf.pos++] = low & 0x7f | 0x80; low >>>= 7;
47028             pbf.buf[pbf.pos++] = low & 0x7f | 0x80; low >>>= 7;
47029             pbf.buf[pbf.pos++] = low & 0x7f | 0x80; low >>>= 7;
47030             pbf.buf[pbf.pos++] = low & 0x7f | 0x80; low >>>= 7;
47031             pbf.buf[pbf.pos]   = low & 0x7f;
47032         }
47033
47034         function writeBigVarintHigh(high, pbf) {
47035             var lsb = (high & 0x07) << 4;
47036
47037             pbf.buf[pbf.pos++] |= lsb         | ((high >>>= 3) ? 0x80 : 0); if (!high) { return; }
47038             pbf.buf[pbf.pos++]  = high & 0x7f | ((high >>>= 7) ? 0x80 : 0); if (!high) { return; }
47039             pbf.buf[pbf.pos++]  = high & 0x7f | ((high >>>= 7) ? 0x80 : 0); if (!high) { return; }
47040             pbf.buf[pbf.pos++]  = high & 0x7f | ((high >>>= 7) ? 0x80 : 0); if (!high) { return; }
47041             pbf.buf[pbf.pos++]  = high & 0x7f | ((high >>>= 7) ? 0x80 : 0); if (!high) { return; }
47042             pbf.buf[pbf.pos++]  = high & 0x7f;
47043         }
47044
47045         function makeRoomForExtraLength(startPos, len, pbf) {
47046             var extraLen =
47047                 len <= 0x3fff ? 1 :
47048                 len <= 0x1fffff ? 2 :
47049                 len <= 0xfffffff ? 3 : Math.floor(Math.log(len) / (Math.LN2 * 7));
47050
47051             // if 1 byte isn't enough for encoding message length, shift the data to the right
47052             pbf.realloc(extraLen);
47053             for (var i = pbf.pos - 1; i >= startPos; i--) { pbf.buf[i + extraLen] = pbf.buf[i]; }
47054         }
47055
47056         function writePackedVarint(arr, pbf)   { for (var i = 0; i < arr.length; i++) { pbf.writeVarint(arr[i]); }   }
47057         function writePackedSVarint(arr, pbf)  { for (var i = 0; i < arr.length; i++) { pbf.writeSVarint(arr[i]); }  }
47058         function writePackedFloat(arr, pbf)    { for (var i = 0; i < arr.length; i++) { pbf.writeFloat(arr[i]); }    }
47059         function writePackedDouble(arr, pbf)   { for (var i = 0; i < arr.length; i++) { pbf.writeDouble(arr[i]); }   }
47060         function writePackedBoolean(arr, pbf)  { for (var i = 0; i < arr.length; i++) { pbf.writeBoolean(arr[i]); }  }
47061         function writePackedFixed32(arr, pbf)  { for (var i = 0; i < arr.length; i++) { pbf.writeFixed32(arr[i]); }  }
47062         function writePackedSFixed32(arr, pbf) { for (var i = 0; i < arr.length; i++) { pbf.writeSFixed32(arr[i]); } }
47063         function writePackedFixed64(arr, pbf)  { for (var i = 0; i < arr.length; i++) { pbf.writeFixed64(arr[i]); }  }
47064         function writePackedSFixed64(arr, pbf) { for (var i = 0; i < arr.length; i++) { pbf.writeSFixed64(arr[i]); } }
47065
47066         // Buffer code below from https://github.com/feross/buffer, MIT-licensed
47067
47068         function readUInt32(buf, pos) {
47069             return ((buf[pos]) |
47070                 (buf[pos + 1] << 8) |
47071                 (buf[pos + 2] << 16)) +
47072                 (buf[pos + 3] * 0x1000000);
47073         }
47074
47075         function writeInt32(buf, val, pos) {
47076             buf[pos] = val;
47077             buf[pos + 1] = (val >>> 8);
47078             buf[pos + 2] = (val >>> 16);
47079             buf[pos + 3] = (val >>> 24);
47080         }
47081
47082         function readInt32(buf, pos) {
47083             return ((buf[pos]) |
47084                 (buf[pos + 1] << 8) |
47085                 (buf[pos + 2] << 16)) +
47086                 (buf[pos + 3] << 24);
47087         }
47088
47089         function readUtf8(buf, pos, end) {
47090             var str = '';
47091             var i = pos;
47092
47093             while (i < end) {
47094                 var b0 = buf[i];
47095                 var c = null; // codepoint
47096                 var bytesPerSequence =
47097                     b0 > 0xEF ? 4 :
47098                     b0 > 0xDF ? 3 :
47099                     b0 > 0xBF ? 2 : 1;
47100
47101                 if (i + bytesPerSequence > end) { break; }
47102
47103                 var b1, b2, b3;
47104
47105                 if (bytesPerSequence === 1) {
47106                     if (b0 < 0x80) {
47107                         c = b0;
47108                     }
47109                 } else if (bytesPerSequence === 2) {
47110                     b1 = buf[i + 1];
47111                     if ((b1 & 0xC0) === 0x80) {
47112                         c = (b0 & 0x1F) << 0x6 | (b1 & 0x3F);
47113                         if (c <= 0x7F) {
47114                             c = null;
47115                         }
47116                     }
47117                 } else if (bytesPerSequence === 3) {
47118                     b1 = buf[i + 1];
47119                     b2 = buf[i + 2];
47120                     if ((b1 & 0xC0) === 0x80 && (b2 & 0xC0) === 0x80) {
47121                         c = (b0 & 0xF) << 0xC | (b1 & 0x3F) << 0x6 | (b2 & 0x3F);
47122                         if (c <= 0x7FF || (c >= 0xD800 && c <= 0xDFFF)) {
47123                             c = null;
47124                         }
47125                     }
47126                 } else if (bytesPerSequence === 4) {
47127                     b1 = buf[i + 1];
47128                     b2 = buf[i + 2];
47129                     b3 = buf[i + 3];
47130                     if ((b1 & 0xC0) === 0x80 && (b2 & 0xC0) === 0x80 && (b3 & 0xC0) === 0x80) {
47131                         c = (b0 & 0xF) << 0x12 | (b1 & 0x3F) << 0xC | (b2 & 0x3F) << 0x6 | (b3 & 0x3F);
47132                         if (c <= 0xFFFF || c >= 0x110000) {
47133                             c = null;
47134                         }
47135                     }
47136                 }
47137
47138                 if (c === null) {
47139                     c = 0xFFFD;
47140                     bytesPerSequence = 1;
47141
47142                 } else if (c > 0xFFFF) {
47143                     c -= 0x10000;
47144                     str += String.fromCharCode(c >>> 10 & 0x3FF | 0xD800);
47145                     c = 0xDC00 | c & 0x3FF;
47146                 }
47147
47148                 str += String.fromCharCode(c);
47149                 i += bytesPerSequence;
47150             }
47151
47152             return str;
47153         }
47154
47155         function readUtf8TextDecoder(buf, pos, end) {
47156             return utf8TextDecoder.decode(buf.subarray(pos, end));
47157         }
47158
47159         function writeUtf8(buf, str, pos) {
47160             for (var i = 0, c, lead; i < str.length; i++) {
47161                 c = str.charCodeAt(i); // code point
47162
47163                 if (c > 0xD7FF && c < 0xE000) {
47164                     if (lead) {
47165                         if (c < 0xDC00) {
47166                             buf[pos++] = 0xEF;
47167                             buf[pos++] = 0xBF;
47168                             buf[pos++] = 0xBD;
47169                             lead = c;
47170                             continue;
47171                         } else {
47172                             c = lead - 0xD800 << 10 | c - 0xDC00 | 0x10000;
47173                             lead = null;
47174                         }
47175                     } else {
47176                         if (c > 0xDBFF || (i + 1 === str.length)) {
47177                             buf[pos++] = 0xEF;
47178                             buf[pos++] = 0xBF;
47179                             buf[pos++] = 0xBD;
47180                         } else {
47181                             lead = c;
47182                         }
47183                         continue;
47184                     }
47185                 } else if (lead) {
47186                     buf[pos++] = 0xEF;
47187                     buf[pos++] = 0xBF;
47188                     buf[pos++] = 0xBD;
47189                     lead = null;
47190                 }
47191
47192                 if (c < 0x80) {
47193                     buf[pos++] = c;
47194                 } else {
47195                     if (c < 0x800) {
47196                         buf[pos++] = c >> 0x6 | 0xC0;
47197                     } else {
47198                         if (c < 0x10000) {
47199                             buf[pos++] = c >> 0xC | 0xE0;
47200                         } else {
47201                             buf[pos++] = c >> 0x12 | 0xF0;
47202                             buf[pos++] = c >> 0xC & 0x3F | 0x80;
47203                         }
47204                         buf[pos++] = c >> 0x6 & 0x3F | 0x80;
47205                     }
47206                     buf[pos++] = c & 0x3F | 0x80;
47207                 }
47208             }
47209             return pos;
47210         }
47211
47212         var pointGeometry = Point;
47213
47214         /**
47215          * A standalone point geometry with useful accessor, comparison, and
47216          * modification methods.
47217          *
47218          * @class Point
47219          * @param {Number} x the x-coordinate. this could be longitude or screen
47220          * pixels, or any other sort of unit.
47221          * @param {Number} y the y-coordinate. this could be latitude or screen
47222          * pixels, or any other sort of unit.
47223          * @example
47224          * var point = new Point(-77, 38);
47225          */
47226         function Point(x, y) {
47227             this.x = x;
47228             this.y = y;
47229         }
47230
47231         Point.prototype = {
47232
47233             /**
47234              * Clone this point, returning a new point that can be modified
47235              * without affecting the old one.
47236              * @return {Point} the clone
47237              */
47238             clone: function() { return new Point(this.x, this.y); },
47239
47240             /**
47241              * Add this point's x & y coordinates to another point,
47242              * yielding a new point.
47243              * @param {Point} p the other point
47244              * @return {Point} output point
47245              */
47246             add:     function(p) { return this.clone()._add(p); },
47247
47248             /**
47249              * Subtract this point's x & y coordinates to from point,
47250              * yielding a new point.
47251              * @param {Point} p the other point
47252              * @return {Point} output point
47253              */
47254             sub:     function(p) { return this.clone()._sub(p); },
47255
47256             /**
47257              * Multiply this point's x & y coordinates by point,
47258              * yielding a new point.
47259              * @param {Point} p the other point
47260              * @return {Point} output point
47261              */
47262             multByPoint:    function(p) { return this.clone()._multByPoint(p); },
47263
47264             /**
47265              * Divide this point's x & y coordinates by point,
47266              * yielding a new point.
47267              * @param {Point} p the other point
47268              * @return {Point} output point
47269              */
47270             divByPoint:     function(p) { return this.clone()._divByPoint(p); },
47271
47272             /**
47273              * Multiply this point's x & y coordinates by a factor,
47274              * yielding a new point.
47275              * @param {Point} k factor
47276              * @return {Point} output point
47277              */
47278             mult:    function(k) { return this.clone()._mult(k); },
47279
47280             /**
47281              * Divide this point's x & y coordinates by a factor,
47282              * yielding a new point.
47283              * @param {Point} k factor
47284              * @return {Point} output point
47285              */
47286             div:     function(k) { return this.clone()._div(k); },
47287
47288             /**
47289              * Rotate this point around the 0, 0 origin by an angle a,
47290              * given in radians
47291              * @param {Number} a angle to rotate around, in radians
47292              * @return {Point} output point
47293              */
47294             rotate:  function(a) { return this.clone()._rotate(a); },
47295
47296             /**
47297              * Rotate this point around p point by an angle a,
47298              * given in radians
47299              * @param {Number} a angle to rotate around, in radians
47300              * @param {Point} p Point to rotate around
47301              * @return {Point} output point
47302              */
47303             rotateAround:  function(a,p) { return this.clone()._rotateAround(a,p); },
47304
47305             /**
47306              * Multiply this point by a 4x1 transformation matrix
47307              * @param {Array<Number>} m transformation matrix
47308              * @return {Point} output point
47309              */
47310             matMult: function(m) { return this.clone()._matMult(m); },
47311
47312             /**
47313              * Calculate this point but as a unit vector from 0, 0, meaning
47314              * that the distance from the resulting point to the 0, 0
47315              * coordinate will be equal to 1 and the angle from the resulting
47316              * point to the 0, 0 coordinate will be the same as before.
47317              * @return {Point} unit vector point
47318              */
47319             unit:    function() { return this.clone()._unit(); },
47320
47321             /**
47322              * Compute a perpendicular point, where the new y coordinate
47323              * is the old x coordinate and the new x coordinate is the old y
47324              * coordinate multiplied by -1
47325              * @return {Point} perpendicular point
47326              */
47327             perp:    function() { return this.clone()._perp(); },
47328
47329             /**
47330              * Return a version of this point with the x & y coordinates
47331              * rounded to integers.
47332              * @return {Point} rounded point
47333              */
47334             round:   function() { return this.clone()._round(); },
47335
47336             /**
47337              * Return the magitude of this point: this is the Euclidean
47338              * distance from the 0, 0 coordinate to this point's x and y
47339              * coordinates.
47340              * @return {Number} magnitude
47341              */
47342             mag: function() {
47343                 return Math.sqrt(this.x * this.x + this.y * this.y);
47344             },
47345
47346             /**
47347              * Judge whether this point is equal to another point, returning
47348              * true or false.
47349              * @param {Point} other the other point
47350              * @return {boolean} whether the points are equal
47351              */
47352             equals: function(other) {
47353                 return this.x === other.x &&
47354                        this.y === other.y;
47355             },
47356
47357             /**
47358              * Calculate the distance from this point to another point
47359              * @param {Point} p the other point
47360              * @return {Number} distance
47361              */
47362             dist: function(p) {
47363                 return Math.sqrt(this.distSqr(p));
47364             },
47365
47366             /**
47367              * Calculate the distance from this point to another point,
47368              * without the square root step. Useful if you're comparing
47369              * relative distances.
47370              * @param {Point} p the other point
47371              * @return {Number} distance
47372              */
47373             distSqr: function(p) {
47374                 var dx = p.x - this.x,
47375                     dy = p.y - this.y;
47376                 return dx * dx + dy * dy;
47377             },
47378
47379             /**
47380              * Get the angle from the 0, 0 coordinate to this point, in radians
47381              * coordinates.
47382              * @return {Number} angle
47383              */
47384             angle: function() {
47385                 return Math.atan2(this.y, this.x);
47386             },
47387
47388             /**
47389              * Get the angle from this point to another point, in radians
47390              * @param {Point} b the other point
47391              * @return {Number} angle
47392              */
47393             angleTo: function(b) {
47394                 return Math.atan2(this.y - b.y, this.x - b.x);
47395             },
47396
47397             /**
47398              * Get the angle between this point and another point, in radians
47399              * @param {Point} b the other point
47400              * @return {Number} angle
47401              */
47402             angleWith: function(b) {
47403                 return this.angleWithSep(b.x, b.y);
47404             },
47405
47406             /*
47407              * Find the angle of the two vectors, solving the formula for
47408              * the cross product a x b = |a||b|sin(θ) for θ.
47409              * @param {Number} x the x-coordinate
47410              * @param {Number} y the y-coordinate
47411              * @return {Number} the angle in radians
47412              */
47413             angleWithSep: function(x, y) {
47414                 return Math.atan2(
47415                     this.x * y - this.y * x,
47416                     this.x * x + this.y * y);
47417             },
47418
47419             _matMult: function(m) {
47420                 var x = m[0] * this.x + m[1] * this.y,
47421                     y = m[2] * this.x + m[3] * this.y;
47422                 this.x = x;
47423                 this.y = y;
47424                 return this;
47425             },
47426
47427             _add: function(p) {
47428                 this.x += p.x;
47429                 this.y += p.y;
47430                 return this;
47431             },
47432
47433             _sub: function(p) {
47434                 this.x -= p.x;
47435                 this.y -= p.y;
47436                 return this;
47437             },
47438
47439             _mult: function(k) {
47440                 this.x *= k;
47441                 this.y *= k;
47442                 return this;
47443             },
47444
47445             _div: function(k) {
47446                 this.x /= k;
47447                 this.y /= k;
47448                 return this;
47449             },
47450
47451             _multByPoint: function(p) {
47452                 this.x *= p.x;
47453                 this.y *= p.y;
47454                 return this;
47455             },
47456
47457             _divByPoint: function(p) {
47458                 this.x /= p.x;
47459                 this.y /= p.y;
47460                 return this;
47461             },
47462
47463             _unit: function() {
47464                 this._div(this.mag());
47465                 return this;
47466             },
47467
47468             _perp: function() {
47469                 var y = this.y;
47470                 this.y = this.x;
47471                 this.x = -y;
47472                 return this;
47473             },
47474
47475             _rotate: function(angle) {
47476                 var cos = Math.cos(angle),
47477                     sin = Math.sin(angle),
47478                     x = cos * this.x - sin * this.y,
47479                     y = sin * this.x + cos * this.y;
47480                 this.x = x;
47481                 this.y = y;
47482                 return this;
47483             },
47484
47485             _rotateAround: function(angle, p) {
47486                 var cos = Math.cos(angle),
47487                     sin = Math.sin(angle),
47488                     x = p.x + cos * (this.x - p.x) - sin * (this.y - p.y),
47489                     y = p.y + sin * (this.x - p.x) + cos * (this.y - p.y);
47490                 this.x = x;
47491                 this.y = y;
47492                 return this;
47493             },
47494
47495             _round: function() {
47496                 this.x = Math.round(this.x);
47497                 this.y = Math.round(this.y);
47498                 return this;
47499             }
47500         };
47501
47502         /**
47503          * Construct a point from an array if necessary, otherwise if the input
47504          * is already a Point, or an unknown type, return it unchanged
47505          * @param {Array<Number>|Point|*} a any kind of input value
47506          * @return {Point} constructed point, or passed-through value.
47507          * @example
47508          * // this
47509          * var point = Point.convert([0, 1]);
47510          * // is equivalent to
47511          * var point = new Point(0, 1);
47512          */
47513         Point.convert = function (a) {
47514             if (a instanceof Point) {
47515                 return a;
47516             }
47517             if (Array.isArray(a)) {
47518                 return new Point(a[0], a[1]);
47519             }
47520             return a;
47521         };
47522
47523         var vectortilefeature = VectorTileFeature;
47524
47525         function VectorTileFeature(pbf, end, extent, keys, values) {
47526             // Public
47527             this.properties = {};
47528             this.extent = extent;
47529             this.type = 0;
47530
47531             // Private
47532             this._pbf = pbf;
47533             this._geometry = -1;
47534             this._keys = keys;
47535             this._values = values;
47536
47537             pbf.readFields(readFeature, this, end);
47538         }
47539
47540         function readFeature(tag, feature, pbf) {
47541             if (tag == 1) { feature.id = pbf.readVarint(); }
47542             else if (tag == 2) { readTag(pbf, feature); }
47543             else if (tag == 3) { feature.type = pbf.readVarint(); }
47544             else if (tag == 4) { feature._geometry = pbf.pos; }
47545         }
47546
47547         function readTag(pbf, feature) {
47548             var end = pbf.readVarint() + pbf.pos;
47549
47550             while (pbf.pos < end) {
47551                 var key = feature._keys[pbf.readVarint()],
47552                     value = feature._values[pbf.readVarint()];
47553                 feature.properties[key] = value;
47554             }
47555         }
47556
47557         VectorTileFeature.types = ['Unknown', 'Point', 'LineString', 'Polygon'];
47558
47559         VectorTileFeature.prototype.loadGeometry = function() {
47560             var pbf = this._pbf;
47561             pbf.pos = this._geometry;
47562
47563             var end = pbf.readVarint() + pbf.pos,
47564                 cmd = 1,
47565                 length = 0,
47566                 x = 0,
47567                 y = 0,
47568                 lines = [],
47569                 line;
47570
47571             while (pbf.pos < end) {
47572                 if (length <= 0) {
47573                     var cmdLen = pbf.readVarint();
47574                     cmd = cmdLen & 0x7;
47575                     length = cmdLen >> 3;
47576                 }
47577
47578                 length--;
47579
47580                 if (cmd === 1 || cmd === 2) {
47581                     x += pbf.readSVarint();
47582                     y += pbf.readSVarint();
47583
47584                     if (cmd === 1) { // moveTo
47585                         if (line) { lines.push(line); }
47586                         line = [];
47587                     }
47588
47589                     line.push(new pointGeometry(x, y));
47590
47591                 } else if (cmd === 7) {
47592
47593                     // Workaround for https://github.com/mapbox/mapnik-vector-tile/issues/90
47594                     if (line) {
47595                         line.push(line[0].clone()); // closePolygon
47596                     }
47597
47598                 } else {
47599                     throw new Error('unknown command ' + cmd);
47600                 }
47601             }
47602
47603             if (line) { lines.push(line); }
47604
47605             return lines;
47606         };
47607
47608         VectorTileFeature.prototype.bbox = function() {
47609             var pbf = this._pbf;
47610             pbf.pos = this._geometry;
47611
47612             var end = pbf.readVarint() + pbf.pos,
47613                 cmd = 1,
47614                 length = 0,
47615                 x = 0,
47616                 y = 0,
47617                 x1 = Infinity,
47618                 x2 = -Infinity,
47619                 y1 = Infinity,
47620                 y2 = -Infinity;
47621
47622             while (pbf.pos < end) {
47623                 if (length <= 0) {
47624                     var cmdLen = pbf.readVarint();
47625                     cmd = cmdLen & 0x7;
47626                     length = cmdLen >> 3;
47627                 }
47628
47629                 length--;
47630
47631                 if (cmd === 1 || cmd === 2) {
47632                     x += pbf.readSVarint();
47633                     y += pbf.readSVarint();
47634                     if (x < x1) { x1 = x; }
47635                     if (x > x2) { x2 = x; }
47636                     if (y < y1) { y1 = y; }
47637                     if (y > y2) { y2 = y; }
47638
47639                 } else if (cmd !== 7) {
47640                     throw new Error('unknown command ' + cmd);
47641                 }
47642             }
47643
47644             return [x1, y1, x2, y2];
47645         };
47646
47647         VectorTileFeature.prototype.toGeoJSON = function(x, y, z) {
47648             var size = this.extent * Math.pow(2, z),
47649                 x0 = this.extent * x,
47650                 y0 = this.extent * y,
47651                 coords = this.loadGeometry(),
47652                 type = VectorTileFeature.types[this.type],
47653                 i, j;
47654
47655             function project(line) {
47656                 for (var j = 0; j < line.length; j++) {
47657                     var p = line[j], y2 = 180 - (p.y + y0) * 360 / size;
47658                     line[j] = [
47659                         (p.x + x0) * 360 / size - 180,
47660                         360 / Math.PI * Math.atan(Math.exp(y2 * Math.PI / 180)) - 90
47661                     ];
47662                 }
47663             }
47664
47665             switch (this.type) {
47666             case 1:
47667                 var points = [];
47668                 for (i = 0; i < coords.length; i++) {
47669                     points[i] = coords[i][0];
47670                 }
47671                 coords = points;
47672                 project(coords);
47673                 break;
47674
47675             case 2:
47676                 for (i = 0; i < coords.length; i++) {
47677                     project(coords[i]);
47678                 }
47679                 break;
47680
47681             case 3:
47682                 coords = classifyRings(coords);
47683                 for (i = 0; i < coords.length; i++) {
47684                     for (j = 0; j < coords[i].length; j++) {
47685                         project(coords[i][j]);
47686                     }
47687                 }
47688                 break;
47689             }
47690
47691             if (coords.length === 1) {
47692                 coords = coords[0];
47693             } else {
47694                 type = 'Multi' + type;
47695             }
47696
47697             var result = {
47698                 type: "Feature",
47699                 geometry: {
47700                     type: type,
47701                     coordinates: coords
47702                 },
47703                 properties: this.properties
47704             };
47705
47706             if ('id' in this) {
47707                 result.id = this.id;
47708             }
47709
47710             return result;
47711         };
47712
47713         // classifies an array of rings into polygons with outer rings and holes
47714
47715         function classifyRings(rings) {
47716             var len = rings.length;
47717
47718             if (len <= 1) { return [rings]; }
47719
47720             var polygons = [],
47721                 polygon,
47722                 ccw;
47723
47724             for (var i = 0; i < len; i++) {
47725                 var area = signedArea$1(rings[i]);
47726                 if (area === 0) { continue; }
47727
47728                 if (ccw === undefined) { ccw = area < 0; }
47729
47730                 if (ccw === area < 0) {
47731                     if (polygon) { polygons.push(polygon); }
47732                     polygon = [rings[i]];
47733
47734                 } else {
47735                     polygon.push(rings[i]);
47736                 }
47737             }
47738             if (polygon) { polygons.push(polygon); }
47739
47740             return polygons;
47741         }
47742
47743         function signedArea$1(ring) {
47744             var sum = 0;
47745             for (var i = 0, len = ring.length, j = len - 1, p1, p2; i < len; j = i++) {
47746                 p1 = ring[i];
47747                 p2 = ring[j];
47748                 sum += (p2.x - p1.x) * (p1.y + p2.y);
47749             }
47750             return sum;
47751         }
47752
47753         var vectortilelayer = VectorTileLayer;
47754
47755         function VectorTileLayer(pbf, end) {
47756             // Public
47757             this.version = 1;
47758             this.name = null;
47759             this.extent = 4096;
47760             this.length = 0;
47761
47762             // Private
47763             this._pbf = pbf;
47764             this._keys = [];
47765             this._values = [];
47766             this._features = [];
47767
47768             pbf.readFields(readLayer, this, end);
47769
47770             this.length = this._features.length;
47771         }
47772
47773         function readLayer(tag, layer, pbf) {
47774             if (tag === 15) { layer.version = pbf.readVarint(); }
47775             else if (tag === 1) { layer.name = pbf.readString(); }
47776             else if (tag === 5) { layer.extent = pbf.readVarint(); }
47777             else if (tag === 2) { layer._features.push(pbf.pos); }
47778             else if (tag === 3) { layer._keys.push(pbf.readString()); }
47779             else if (tag === 4) { layer._values.push(readValueMessage(pbf)); }
47780         }
47781
47782         function readValueMessage(pbf) {
47783             var value = null,
47784                 end = pbf.readVarint() + pbf.pos;
47785
47786             while (pbf.pos < end) {
47787                 var tag = pbf.readVarint() >> 3;
47788
47789                 value = tag === 1 ? pbf.readString() :
47790                     tag === 2 ? pbf.readFloat() :
47791                     tag === 3 ? pbf.readDouble() :
47792                     tag === 4 ? pbf.readVarint64() :
47793                     tag === 5 ? pbf.readVarint() :
47794                     tag === 6 ? pbf.readSVarint() :
47795                     tag === 7 ? pbf.readBoolean() : null;
47796             }
47797
47798             return value;
47799         }
47800
47801         // return feature `i` from this layer as a `VectorTileFeature`
47802         VectorTileLayer.prototype.feature = function(i) {
47803             if (i < 0 || i >= this._features.length) { throw new Error('feature index out of bounds'); }
47804
47805             this._pbf.pos = this._features[i];
47806
47807             var end = this._pbf.readVarint() + this._pbf.pos;
47808             return new vectortilefeature(this._pbf, end, this.extent, this._keys, this._values);
47809         };
47810
47811         var vectortile = VectorTile;
47812
47813         function VectorTile(pbf, end) {
47814             this.layers = pbf.readFields(readTile, {}, end);
47815         }
47816
47817         function readTile(tag, layers, pbf) {
47818             if (tag === 3) {
47819                 var layer = new vectortilelayer(pbf, pbf.readVarint() + pbf.pos);
47820                 if (layer.length) { layers[layer.name] = layer; }
47821             }
47822         }
47823
47824         var VectorTile$1 = vectortile;
47825         var VectorTileFeature$1 = vectortilefeature;
47826         var VectorTileLayer$1 = vectortilelayer;
47827
47828         var vectorTile = {
47829                 VectorTile: VectorTile$1,
47830                 VectorTileFeature: VectorTileFeature$1,
47831                 VectorTileLayer: VectorTileLayer$1
47832         };
47833
47834         var tiler$7 = utilTiler().tileSize(512).margin(1);
47835         var dispatch$8 = dispatch('loadedData');
47836         var _vtCache;
47837
47838
47839         function abortRequest$7(controller) {
47840             controller.abort();
47841         }
47842
47843
47844         function vtToGeoJSON(data, tile, mergeCache) {
47845             var vectorTile$1 = new vectorTile.VectorTile(new pbf(data));
47846             var layers = Object.keys(vectorTile$1.layers);
47847             if (!Array.isArray(layers)) { layers = [layers]; }
47848
47849             var features = [];
47850             layers.forEach(function(layerID) {
47851                 var layer = vectorTile$1.layers[layerID];
47852                 if (layer) {
47853                     for (var i = 0; i < layer.length; i++) {
47854                         var feature = layer.feature(i).toGeoJSON(tile.xyz[0], tile.xyz[1], tile.xyz[2]);
47855                         var geometry = feature.geometry;
47856
47857                         // Treat all Polygons as MultiPolygons
47858                         if (geometry.type === 'Polygon') {
47859                             geometry.type = 'MultiPolygon';
47860                             geometry.coordinates = [geometry.coordinates];
47861                         }
47862
47863                         // Clip to tile bounds
47864                         if (geometry.type === 'MultiPolygon') {
47865                             var isClipped = false;
47866                             var featureClip = turf_bboxClip(feature, tile.extent.rectangle());
47867                             if (!fastDeepEqual(feature.geometry, featureClip.geometry)) {
47868                                 // feature = featureClip;
47869                                 isClipped = true;
47870                             }
47871                             if (!feature.geometry.coordinates.length) { continue; }   // not actually on this tile
47872                             if (!feature.geometry.coordinates[0].length) { continue; }   // not actually on this tile
47873                         }
47874
47875                         // Generate some unique IDs and add some metadata
47876                         var featurehash = utilHashcode(fastJsonStableStringify(feature));
47877                         var propertyhash = utilHashcode(fastJsonStableStringify(feature.properties || {}));
47878                         feature.__layerID__ = layerID.replace(/[^_a-zA-Z0-9\-]/g, '_');
47879                         feature.__featurehash__ = featurehash;
47880                         feature.__propertyhash__ = propertyhash;
47881                         features.push(feature);
47882
47883                         // Clipped Polygons at same zoom with identical properties can get merged
47884                         if (isClipped && geometry.type === 'MultiPolygon') {
47885                             var merged = mergeCache[propertyhash];
47886                             if (merged && merged.length) {
47887                                 var other = merged[0];
47888                                 var coords = union(
47889                                     feature.geometry.coordinates,
47890                                     other.geometry.coordinates
47891                                 );
47892
47893                                 if (!coords || !coords.length) {
47894                                     continue;  // something failed in martinez union
47895                                 }
47896
47897                                 merged.push(feature);
47898                                 for (var j = 0; j < merged.length; j++) {      // all these features get...
47899                                     merged[j].geometry.coordinates = coords;   // same coords
47900                                     merged[j].__featurehash__ = featurehash;   // same hash, so deduplication works
47901                                 }
47902                             } else {
47903                                 mergeCache[propertyhash] = [feature];
47904                             }
47905                         }
47906                     }
47907                 }
47908             });
47909
47910             return features;
47911         }
47912
47913
47914         function loadTile(source, tile) {
47915             if (source.loaded[tile.id] || source.inflight[tile.id]) { return; }
47916
47917             var url = source.template
47918                 .replace('{x}', tile.xyz[0])
47919                 .replace('{y}', tile.xyz[1])
47920                 // TMS-flipped y coordinate
47921                 .replace(/\{[t-]y\}/, Math.pow(2, tile.xyz[2]) - tile.xyz[1] - 1)
47922                 .replace(/\{z(oom)?\}/, tile.xyz[2])
47923                 .replace(/\{switch:([^}]+)\}/, function(s, r) {
47924                     var subdomains = r.split(',');
47925                     return subdomains[(tile.xyz[0] + tile.xyz[1]) % subdomains.length];
47926                 });
47927
47928
47929             var controller = new AbortController();
47930             source.inflight[tile.id] = controller;
47931
47932             fetch(url, { signal: controller.signal })
47933                 .then(function(response) {
47934                     if (!response.ok) {
47935                         throw new Error(response.status + ' ' + response.statusText);
47936                     }
47937                     source.loaded[tile.id] = [];
47938                     delete source.inflight[tile.id];
47939                     return response.arrayBuffer();
47940                 })
47941                 .then(function(data) {
47942                     if (!data) {
47943                         throw new Error('No Data');
47944                     }
47945
47946                     var z = tile.xyz[2];
47947                     if (!source.canMerge[z]) {
47948                         source.canMerge[z] = {};  // initialize mergeCache
47949                     }
47950
47951                     source.loaded[tile.id] = vtToGeoJSON(data, tile, source.canMerge[z]);
47952                     dispatch$8.call('loadedData');
47953                 })
47954                 .catch(function() {
47955                     source.loaded[tile.id] = [];
47956                     delete source.inflight[tile.id];
47957                 });
47958         }
47959
47960
47961         var serviceVectorTile = {
47962
47963             init: function() {
47964                 if (!_vtCache) {
47965                     this.reset();
47966                 }
47967
47968                 this.event = utilRebind(this, dispatch$8, 'on');
47969             },
47970
47971
47972             reset: function() {
47973                 for (var sourceID in _vtCache) {
47974                     var source = _vtCache[sourceID];
47975                     if (source && source.inflight) {
47976                         Object.values(source.inflight).forEach(abortRequest$7);
47977                     }
47978                 }
47979
47980                 _vtCache = {};
47981             },
47982
47983
47984             addSource: function(sourceID, template) {
47985                 _vtCache[sourceID] = { template: template, inflight: {}, loaded: {}, canMerge: {} };
47986                 return _vtCache[sourceID];
47987             },
47988
47989
47990             data: function(sourceID, projection) {
47991                 var source = _vtCache[sourceID];
47992                 if (!source) { return []; }
47993
47994                 var tiles = tiler$7.getTiles(projection);
47995                 var seen = {};
47996                 var results = [];
47997
47998                 for (var i = 0; i < tiles.length; i++) {
47999                     var features = source.loaded[tiles[i].id];
48000                     if (!features || !features.length) { continue; }
48001
48002                     for (var j = 0; j < features.length; j++) {
48003                         var feature = features[j];
48004                         var hash = feature.__featurehash__;
48005                         if (seen[hash]) { continue; }
48006                         seen[hash] = true;
48007
48008                         // return a shallow copy, because the hash may change
48009                         // later if this feature gets merged with another
48010                         results.push(Object.assign({}, feature));  // shallow copy
48011                     }
48012                 }
48013
48014                 return results;
48015             },
48016
48017
48018             loadTiles: function(sourceID, template, projection) {
48019                 var source = _vtCache[sourceID];
48020                 if (!source) {
48021                     source = this.addSource(sourceID, template);
48022                 }
48023
48024                 var tiles = tiler$7.getTiles(projection);
48025
48026                 // abort inflight requests that are no longer needed
48027                 Object.keys(source.inflight).forEach(function(k) {
48028                     var wanted = tiles.find(function(tile) { return k === tile.id; });
48029                     if (!wanted) {
48030                         abortRequest$7(source.inflight[k]);
48031                         delete source.inflight[k];
48032                     }
48033                 });
48034
48035                 tiles.forEach(function(tile) {
48036                     loadTile(source, tile);
48037                 });
48038             },
48039
48040
48041             cache: function() {
48042                 return _vtCache;
48043             }
48044
48045         };
48046
48047         var apibase$5 = 'https://www.wikidata.org/w/api.php?';
48048         var _wikidataCache = {};
48049
48050
48051         var serviceWikidata = {
48052
48053             init: function() {},
48054
48055             reset: function() {
48056                 _wikidataCache = {};
48057             },
48058
48059
48060             // Search for Wikidata items matching the query
48061             itemsForSearchQuery: function(query, callback) {
48062                 if (!query) {
48063                     if (callback) { callback('No query', {}); }
48064                     return;
48065                 }
48066
48067                 var lang = this.languagesToQuery()[0];
48068
48069                 var url = apibase$5 + utilQsString({
48070                     action: 'wbsearchentities',
48071                     format: 'json',
48072                     formatversion: 2,
48073                     search: query,
48074                     type: 'item',
48075                     // the language to search
48076                     language: lang,
48077                     // the language for the label and description in the result
48078                     uselang: lang,
48079                     limit: 10,
48080                     origin: '*'
48081                 });
48082
48083                 d3_json(url)
48084                     .then(function(result) {
48085                         if (result && result.error) {
48086                             throw new Error(result.error);
48087                         }
48088                         if (callback) { callback(null, result.search || {}); }
48089                     })
48090                     .catch(function(err) {
48091                         if (callback) { callback(err.message, {}); }
48092                     });
48093             },
48094
48095
48096             // Given a Wikipedia language and article title,
48097             // return an array of corresponding Wikidata entities.
48098             itemsByTitle: function(lang, title, callback) {
48099                 if (!title) {
48100                     if (callback) { callback('No title', {}); }
48101                     return;
48102                 }
48103
48104                 lang = lang || 'en';
48105                 var url = apibase$5 + utilQsString({
48106                     action: 'wbgetentities',
48107                     format: 'json',
48108                     formatversion: 2,
48109                     sites: lang.replace(/-/g, '_') + 'wiki',
48110                     titles: title,
48111                     languages: 'en', // shrink response by filtering to one language
48112                     origin: '*'
48113                 });
48114
48115                 d3_json(url)
48116                     .then(function(result) {
48117                         if (result && result.error) {
48118                             throw new Error(result.error);
48119                         }
48120                         if (callback) { callback(null, result.entities || {}); }
48121                     })
48122                     .catch(function(err) {
48123                         if (callback) { callback(err.message, {}); }
48124                     });
48125             },
48126
48127
48128             languagesToQuery: function() {
48129                 var localeCode = _mainLocalizer.localeCode().toLowerCase();
48130                 // HACK: en-us isn't a wikidata language. We should really be filtering by
48131                 // the languages known to be supported by wikidata.
48132                 if (localeCode === 'en-us') { localeCode = 'en'; }
48133                 return utilArrayUniq([
48134                     localeCode,
48135                     _mainLocalizer.languageCode().toLowerCase(),
48136                     'en'
48137                 ]);
48138             },
48139
48140
48141             entityByQID: function(qid, callback) {
48142                 if (!qid) {
48143                     callback('No qid', {});
48144                     return;
48145                 }
48146                 if (_wikidataCache[qid]) {
48147                     if (callback) { callback(null, _wikidataCache[qid]); }
48148                     return;
48149                 }
48150
48151                 var langs = this.languagesToQuery();
48152                 var url = apibase$5 + utilQsString({
48153                     action: 'wbgetentities',
48154                     format: 'json',
48155                     formatversion: 2,
48156                     ids: qid,
48157                     props: 'labels|descriptions|claims|sitelinks',
48158                     sitefilter: langs.map(function(d) { return d + 'wiki'; }).join('|'),
48159                     languages: langs.join('|'),
48160                     languagefallback: 1,
48161                     origin: '*'
48162                 });
48163
48164                 d3_json(url)
48165                     .then(function(result) {
48166                         if (result && result.error) {
48167                             throw new Error(result.error);
48168                         }
48169                         if (callback) { callback(null, result.entities[qid] || {}); }
48170                     })
48171                     .catch(function(err) {
48172                         if (callback) { callback(err.message, {}); }
48173                     });
48174             },
48175
48176
48177             // Pass `params` object of the form:
48178             // {
48179             //   qid: 'string'      // brand wikidata  (e.g. 'Q37158')
48180             // }
48181             //
48182             // Get an result object used to display tag documentation
48183             // {
48184             //   title:        'string',
48185             //   description:  'string',
48186             //   editURL:      'string',
48187             //   imageURL:     'string',
48188             //   wiki:         { title: 'string', text: 'string', url: 'string' }
48189             // }
48190             //
48191             getDocs: function(params, callback) {
48192                 var langs = this.languagesToQuery();
48193                 this.entityByQID(params.qid, function(err, entity) {
48194                     if (err || !entity) {
48195                         callback(err || 'No entity');
48196                         return;
48197                     }
48198
48199                     var i;
48200                     var description;
48201                     if (entity.descriptions && Object.keys(entity.descriptions).length > 0) {
48202                         description = entity.descriptions[Object.keys(entity.descriptions)[0]].value;
48203                     }
48204
48205                     // prepare result
48206                     var result = {
48207                         title: entity.id,
48208                         description: description,
48209                         editURL: 'https://www.wikidata.org/wiki/' + entity.id
48210                     };
48211
48212                     // add image
48213                     if (entity.claims) {
48214                         var imageroot = 'https://commons.wikimedia.org/w/index.php';
48215                         var props = ['P154','P18'];  // logo image, image
48216                         var prop, image;
48217                         for (i = 0; i < props.length; i++) {
48218                             prop = entity.claims[props[i]];
48219                             if (prop && Object.keys(prop).length > 0) {
48220                                 image = prop[Object.keys(prop)[0]].mainsnak.datavalue.value;
48221                                 if (image) {
48222                                     result.imageURL = imageroot + '?' + utilQsString({
48223                                         title: 'Special:Redirect/file/' + image,
48224                                         width: 400
48225                                     });
48226                                     break;
48227                                 }
48228                             }
48229                         }
48230                     }
48231
48232                     if (entity.sitelinks) {
48233                         var englishLocale = _mainLocalizer.languageCode().toLowerCase() === 'en';
48234
48235                         // must be one of these that we requested..
48236                         for (i = 0; i < langs.length; i++) {   // check each, in order of preference
48237                             var w = langs[i] + 'wiki';
48238                             if (entity.sitelinks[w]) {
48239                                 var title = entity.sitelinks[w].title;
48240                                 var tKey = 'inspector.wiki_reference';
48241                                 if (!englishLocale && langs[i] === 'en') {   // user's locale isn't English but
48242                                     tKey = 'inspector.wiki_en_reference';    // we are sending them to enwiki anyway..
48243                                 }
48244
48245                                 result.wiki = {
48246                                     title: title,
48247                                     text: tKey,
48248                                     url: 'https://' + langs[i] + '.wikipedia.org/wiki/' + title.replace(/ /g, '_')
48249                                 };
48250                                 break;
48251                             }
48252                         }
48253                     }
48254
48255                     callback(null, result);
48256                 });
48257             }
48258
48259         };
48260
48261         var endpoint = 'https://en.wikipedia.org/w/api.php?';
48262
48263         var serviceWikipedia = {
48264
48265             init: function() {},
48266             reset: function() {},
48267
48268
48269             search: function(lang, query, callback) {
48270                 if (!query) {
48271                     if (callback) { callback('No Query', []); }
48272                     return;
48273                 }
48274
48275                 lang = lang || 'en';
48276                 var url = endpoint.replace('en', lang) +
48277                     utilQsString({
48278                         action: 'query',
48279                         list: 'search',
48280                         srlimit: '10',
48281                         srinfo: 'suggestion',
48282                         format: 'json',
48283                         origin: '*',
48284                         srsearch: query
48285                     });
48286
48287                 d3_json(url)
48288                     .then(function(result) {
48289                         if (result && result.error) {
48290                             throw new Error(result.error);
48291                         } else if (!result || !result.query || !result.query.search) {
48292                             throw new Error('No Results');
48293                         }
48294                         if (callback) {
48295                             var titles = result.query.search.map(function(d) { return d.title; });
48296                             callback(null, titles);
48297                         }
48298                     })
48299                     .catch(function(err) {
48300                         if (callback) { callback(err, []); }
48301                     });
48302             },
48303
48304
48305             suggestions: function(lang, query, callback) {
48306                 if (!query) {
48307                     if (callback) { callback('', []); }
48308                     return;
48309                 }
48310
48311                 lang = lang || 'en';
48312                 var url = endpoint.replace('en', lang) +
48313                     utilQsString({
48314                         action: 'opensearch',
48315                         namespace: 0,
48316                         suggest: '',
48317                         format: 'json',
48318                         origin: '*',
48319                         search: query
48320                     });
48321
48322                 d3_json(url)
48323                     .then(function(result) {
48324                         if (result && result.error) {
48325                             throw new Error(result.error);
48326                         } else if (!result || result.length < 2) {
48327                             throw new Error('No Results');
48328                         }
48329                         if (callback) { callback(null, result[1] || []); }
48330                     })
48331                     .catch(function(err) {
48332                         if (callback) { callback(err.message, []); }
48333                     });
48334             },
48335
48336
48337             translations: function(lang, title, callback) {
48338                 if (!title) {
48339                     if (callback) { callback('No Title'); }
48340                     return;
48341                 }
48342
48343                 var url = endpoint.replace('en', lang) +
48344                     utilQsString({
48345                         action: 'query',
48346                         prop: 'langlinks',
48347                         format: 'json',
48348                         origin: '*',
48349                         lllimit: 500,
48350                         titles: title
48351                     });
48352
48353                 d3_json(url)
48354                     .then(function(result) {
48355                         if (result && result.error) {
48356                             throw new Error(result.error);
48357                         } else if (!result || !result.query || !result.query.pages) {
48358                             throw new Error('No Results');
48359                         }
48360                         if (callback) {
48361                             var list = result.query.pages[Object.keys(result.query.pages)[0]];
48362                             var translations = {};
48363                             if (list && list.langlinks) {
48364                                 list.langlinks.forEach(function(d) { translations[d.lang] = d['*']; });
48365                             }
48366                             callback(null, translations);
48367                         }
48368                     })
48369                     .catch(function(err) {
48370                         if (callback) { callback(err.message); }
48371                     });
48372             }
48373
48374         };
48375
48376         var services = {
48377             geocoder: serviceNominatim,
48378             keepRight: serviceKeepRight,
48379             improveOSM: serviceImproveOSM,
48380             osmose: serviceOsmose,
48381             mapillary: serviceMapillary,
48382             openstreetcam: serviceOpenstreetcam,
48383             osm: serviceOsm,
48384             osmWikibase: serviceOsmWikibase,
48385             maprules: serviceMapRules,
48386             streetside: serviceStreetside,
48387             taginfo: serviceTaginfo,
48388             vectorTile: serviceVectorTile,
48389             wikidata: serviceWikidata,
48390             wikipedia: serviceWikipedia
48391         };
48392
48393         function svgIcon(name, svgklass, useklass) {
48394             return function drawIcon(selection) {
48395                 selection.selectAll('svg.icon' + (svgklass ? '.' + svgklass.split(' ')[0] : ''))
48396                     .data([0])
48397                     .enter()
48398                     .append('svg')
48399                     .attr('class', 'icon ' + (svgklass || ''))
48400                     .append('use')
48401                     .attr('xlink:href', name)
48402                     .attr('class', useklass);
48403             };
48404         }
48405
48406         function uiNoteComments() {
48407             var _note;
48408
48409
48410             function noteComments(selection) {
48411                 if (_note.isNew()) { return; } // don't draw .comments-container
48412
48413                 var comments = selection.selectAll('.comments-container')
48414                     .data([0]);
48415
48416                 comments = comments.enter()
48417                     .append('div')
48418                     .attr('class', 'comments-container')
48419                     .merge(comments);
48420
48421                 var commentEnter = comments.selectAll('.comment')
48422                     .data(_note.comments)
48423                     .enter()
48424                     .append('div')
48425                     .attr('class', 'comment');
48426
48427                 commentEnter
48428                     .append('div')
48429                     .attr('class', function(d) { return 'comment-avatar user-' + d.uid; })
48430                     .call(svgIcon('#iD-icon-avatar', 'comment-avatar-icon'));
48431
48432                 var mainEnter = commentEnter
48433                     .append('div')
48434                     .attr('class', 'comment-main');
48435
48436                 var metadataEnter = mainEnter
48437                     .append('div')
48438                     .attr('class', 'comment-metadata');
48439
48440                 metadataEnter
48441                     .append('div')
48442                     .attr('class', 'comment-author')
48443                     .each(function(d) {
48444                         var selection = select(this);
48445                         var osm = services.osm;
48446                         if (osm && d.user) {
48447                             selection = selection
48448                                 .append('a')
48449                                 .attr('class', 'comment-author-link')
48450                                 .attr('href', osm.userURL(d.user))
48451                                 .attr('tabindex', -1)
48452                                 .attr('target', '_blank');
48453                         }
48454                         selection
48455                             .text(function(d) { return d.user || _t('note.anonymous'); });
48456                     });
48457
48458                 metadataEnter
48459                     .append('div')
48460                     .attr('class', 'comment-date')
48461                     .text(function(d) {
48462                         return _t('note.status.' + d.action, { when: localeDateString(d.date) });
48463                     });
48464
48465                 mainEnter
48466                     .append('div')
48467                     .attr('class', 'comment-text')
48468                     .html(function(d) { return d.html; })
48469                     .selectAll('a')
48470                         .attr('rel', 'noopener nofollow')
48471                         .attr('target', '_blank');
48472
48473                 comments
48474                     .call(replaceAvatars);
48475             }
48476
48477
48478             function replaceAvatars(selection) {
48479                 var showThirdPartyIcons = corePreferences('preferences.privacy.thirdpartyicons') || 'true';
48480                 var osm = services.osm;
48481                 if (showThirdPartyIcons !== 'true' || !osm) { return; }
48482
48483                 var uids = {};  // gather uids in the comment thread
48484                 _note.comments.forEach(function(d) {
48485                     if (d.uid) { uids[d.uid] = true; }
48486                 });
48487
48488                 Object.keys(uids).forEach(function(uid) {
48489                     osm.loadUser(uid, function(err, user) {
48490                         if (!user || !user.image_url) { return; }
48491
48492                         selection.selectAll('.comment-avatar.user-' + uid)
48493                             .html('')
48494                             .append('img')
48495                             .attr('class', 'icon comment-avatar-icon')
48496                             .attr('src', user.image_url)
48497                             .attr('alt', user.display_name);
48498                     });
48499                 });
48500             }
48501
48502
48503             function localeDateString(s) {
48504                 if (!s) { return null; }
48505                 var options = { day: 'numeric', month: 'short', year: 'numeric' };
48506                 s = s.replace(/-/g, '/'); // fix browser-specific Date() issues
48507                 var d = new Date(s);
48508                 if (isNaN(d.getTime())) { return null; }
48509                 return d.toLocaleDateString(_mainLocalizer.localeCode(), options);
48510             }
48511
48512
48513             noteComments.note = function(val) {
48514                 if (!arguments.length) { return _note; }
48515                 _note = val;
48516                 return noteComments;
48517             };
48518
48519
48520             return noteComments;
48521         }
48522
48523         function uiNoteHeader() {
48524             var _note;
48525
48526
48527             function noteHeader(selection) {
48528                 var header = selection.selectAll('.note-header')
48529                     .data(
48530                         (_note ? [_note] : []),
48531                         function(d) { return d.status + d.id; }
48532                     );
48533
48534                 header.exit()
48535                     .remove();
48536
48537                 var headerEnter = header.enter()
48538                     .append('div')
48539                     .attr('class', 'note-header');
48540
48541                 var iconEnter = headerEnter
48542                     .append('div')
48543                     .attr('class', function(d) { return 'note-header-icon ' + d.status; })
48544                     .classed('new', function(d) { return d.id < 0; });
48545
48546                 iconEnter
48547                     .append('div')
48548                     .attr('class', 'preset-icon-28')
48549                     .call(svgIcon('#iD-icon-note', 'note-fill'));
48550
48551                 iconEnter.each(function(d) {
48552                     var statusIcon = '#iD-icon-' + (d.id < 0 ? 'plus' : (d.status === 'open' ? 'close' : 'apply'));
48553                     iconEnter
48554                         .append('div')
48555                         .attr('class', 'note-icon-annotation')
48556                         .call(svgIcon(statusIcon, 'icon-annotation'));
48557                 });
48558
48559                 headerEnter
48560                     .append('div')
48561                     .attr('class', 'note-header-label')
48562                     .text(function(d) {
48563                         if (_note.isNew()) { return _t('note.new'); }
48564                         return _t('note.note') + ' ' + d.id + ' ' +
48565                             (d.status === 'closed' ? _t('note.closed') : '');
48566                     });
48567             }
48568
48569
48570             noteHeader.note = function(val) {
48571                 if (!arguments.length) { return _note; }
48572                 _note = val;
48573                 return noteHeader;
48574             };
48575
48576
48577             return noteHeader;
48578         }
48579
48580         function uiNoteReport() {
48581             var _note;
48582
48583             function noteReport(selection) {
48584                 var url;
48585                 if (services.osm && (_note instanceof osmNote) && (!_note.isNew())) {
48586                     url = services.osm.noteReportURL(_note);
48587                 }
48588
48589                 var link = selection.selectAll('.note-report')
48590                     .data(url ? [url] : []);
48591
48592                 // exit
48593                 link.exit()
48594                     .remove();
48595
48596                 // enter
48597                 var linkEnter = link.enter()
48598                     .append('a')
48599                     .attr('class', 'note-report')
48600                     .attr('target', '_blank')
48601                     .attr('href', function(d) { return d; })
48602                     .call(svgIcon('#iD-icon-out-link', 'inline'));
48603
48604                 linkEnter
48605                     .append('span')
48606                     .text(_t('note.report'));
48607             }
48608
48609
48610             noteReport.note = function(val) {
48611                 if (!arguments.length) { return _note; }
48612                 _note = val;
48613                 return noteReport;
48614             };
48615
48616             return noteReport;
48617         }
48618
48619         function uiViewOnOSM(context) {
48620             var _what;   // an osmEntity or osmNote
48621
48622
48623             function viewOnOSM(selection) {
48624                 var url;
48625                 if (_what instanceof osmEntity) {
48626                     url = context.connection().entityURL(_what);
48627                 } else if (_what instanceof osmNote) {
48628                     url = context.connection().noteURL(_what);
48629                 }
48630
48631                 var data = ((!_what || _what.isNew()) ? [] : [_what]);
48632                 var link = selection.selectAll('.view-on-osm')
48633                     .data(data, function(d) { return d.id; });
48634
48635                 // exit
48636                 link.exit()
48637                     .remove();
48638
48639                 // enter
48640                 var linkEnter = link.enter()
48641                     .append('a')
48642                     .attr('class', 'view-on-osm')
48643                     .attr('target', '_blank')
48644                     .attr('href', url)
48645                     .call(svgIcon('#iD-icon-out-link', 'inline'));
48646
48647                 linkEnter
48648                     .append('span')
48649                     .text(_t('inspector.view_on_osm'));
48650             }
48651
48652
48653             viewOnOSM.what = function(_) {
48654                 if (!arguments.length) { return _what; }
48655                 _what = _;
48656                 return viewOnOSM;
48657             };
48658
48659             return viewOnOSM;
48660         }
48661
48662         function uiNoteEditor(context) {
48663             var dispatch$1 = dispatch('change');
48664             var noteComments = uiNoteComments();
48665             var noteHeader = uiNoteHeader();
48666
48667             // var formFields = uiFormFields(context);
48668
48669             var _note;
48670             var _newNote;
48671             // var _fieldsArr;
48672
48673
48674             function noteEditor(selection) {
48675
48676                 var header = selection.selectAll('.header')
48677                     .data([0]);
48678
48679                 var headerEnter = header.enter()
48680                     .append('div')
48681                     .attr('class', 'header fillL');
48682
48683                 headerEnter
48684                     .append('button')
48685                     .attr('class', 'close')
48686                     .on('click', function() {
48687                         context.enter(modeBrowse(context));
48688                     })
48689                     .call(svgIcon('#iD-icon-close'));
48690
48691                 headerEnter
48692                     .append('h3')
48693                     .text(_t('note.title'));
48694
48695
48696                 var body = selection.selectAll('.body')
48697                     .data([0]);
48698
48699                 body = body.enter()
48700                     .append('div')
48701                     .attr('class', 'body')
48702                     .merge(body);
48703
48704                 var editor = body.selectAll('.note-editor')
48705                     .data([0]);
48706
48707                 editor.enter()
48708                     .append('div')
48709                     .attr('class', 'modal-section note-editor')
48710                     .merge(editor)
48711                     .call(noteHeader.note(_note))
48712                     .call(noteComments.note(_note))
48713                     .call(noteSaveSection);
48714
48715                 var footer = selection.selectAll('.footer')
48716                     .data([0]);
48717
48718                 footer.enter()
48719                     .append('div')
48720                     .attr('class', 'footer')
48721                     .merge(footer)
48722                     .call(uiViewOnOSM(context).what(_note))
48723                     .call(uiNoteReport().note(_note));
48724
48725
48726                 // rerender the note editor on any auth change
48727                 var osm = services.osm;
48728                 if (osm) {
48729                     osm.on('change.note-save', function() {
48730                         selection.call(noteEditor);
48731                     });
48732                 }
48733             }
48734
48735
48736             function noteSaveSection(selection) {
48737                 var isSelected = (_note && _note.id === context.selectedNoteID());
48738                 var noteSave = selection.selectAll('.note-save')
48739                     .data((isSelected ? [_note] : []), function(d) { return d.status + d.id; });
48740
48741                 // exit
48742                 noteSave.exit()
48743                     .remove();
48744
48745                 // enter
48746                 var noteSaveEnter = noteSave.enter()
48747                     .append('div')
48748                     .attr('class', 'note-save save-section cf');
48749
48750                 // // if new note, show categories to pick from
48751                 // if (_note.isNew()) {
48752                 //     var presets = presetManager;
48753
48754                 //     // NOTE: this key isn't a age and therefore there is no documentation (yet)
48755                 //     _fieldsArr = [
48756                 //         uiField(context, presets.field('category'), null, { show: true, revert: false }),
48757                 //     ];
48758
48759                 //     _fieldsArr.forEach(function(field) {
48760                 //         field
48761                 //             .on('change', changeCategory);
48762                 //     });
48763
48764                 //     noteSaveEnter
48765                 //         .append('div')
48766                 //         .attr('class', 'note-category')
48767                 //         .call(formFields.fieldsArr(_fieldsArr));
48768                 // }
48769
48770                 // function changeCategory() {
48771                 //     // NOTE: perhaps there is a better way to get value
48772                 //     var val = context.container().select('input[name=\'category\']:checked').property('__data__') || undefined;
48773
48774                 //     // store the unsaved category with the note itself
48775                 //     _note = _note.update({ newCategory: val });
48776                 //     var osm = services.osm;
48777                 //     if (osm) {
48778                 //         osm.replaceNote(_note);  // update note cache
48779                 //     }
48780                 //     noteSave
48781                 //         .call(noteSaveButtons);
48782                 // }
48783
48784                 noteSaveEnter
48785                     .append('h4')
48786                     .attr('class', '.note-save-header')
48787                     .text(function() {
48788                         return _note.isNew() ? _t('note.newDescription') : _t('note.newComment');
48789                     });
48790
48791                 var commentTextarea = noteSaveEnter
48792                     .append('textarea')
48793                     .attr('class', 'new-comment-input')
48794                     .attr('placeholder', _t('note.inputPlaceholder'))
48795                     .attr('maxlength', 1000)
48796                     .property('value', function(d) { return d.newComment; })
48797                     .call(utilNoAuto)
48798                     .on('keydown.note-input', keydown)
48799                     .on('input.note-input', changeInput)
48800                     .on('blur.note-input', changeInput);
48801
48802                 if (_newNote) {
48803                     // autofocus the comment field for new notes
48804                     commentTextarea.node().focus();
48805                 }
48806
48807                 // update
48808                 noteSave = noteSaveEnter
48809                     .merge(noteSave)
48810                     .call(userDetails)
48811                     .call(noteSaveButtons);
48812
48813
48814                 // fast submit if user presses cmd+enter
48815                 function keydown() {
48816                     if (!(event.keyCode === 13 && event.metaKey)) { return; }
48817
48818                     var osm = services.osm;
48819                     if (!osm) { return; }
48820
48821                     var hasAuth = osm.authenticated();
48822                     if (!hasAuth) { return; }
48823
48824                     if (!_note.newComment) { return; }
48825
48826                     event.preventDefault();
48827
48828                     select(this)
48829                         .on('keydown.note-input', null);
48830
48831                     // focus on button and submit
48832                     window.setTimeout(function() {
48833                         if (_note.isNew()) {
48834                             noteSave.selectAll('.save-button').node().focus();
48835                             clickSave(_note);
48836                         } else  {
48837                             noteSave.selectAll('.comment-button').node().focus();
48838                             clickComment(_note);
48839                         }
48840                     }, 10);
48841                 }
48842
48843
48844                 function changeInput() {
48845                     var input = select(this);
48846                     var val = input.property('value').trim() || undefined;
48847
48848                     // store the unsaved comment with the note itself
48849                     _note = _note.update({ newComment: val });
48850
48851                     var osm = services.osm;
48852                     if (osm) {
48853                         osm.replaceNote(_note);  // update note cache
48854                     }
48855
48856                     noteSave
48857                         .call(noteSaveButtons);
48858                 }
48859             }
48860
48861
48862             function userDetails(selection) {
48863                 var detailSection = selection.selectAll('.detail-section')
48864                     .data([0]);
48865
48866                 detailSection = detailSection.enter()
48867                     .append('div')
48868                     .attr('class', 'detail-section')
48869                     .merge(detailSection);
48870
48871                 var osm = services.osm;
48872                 if (!osm) { return; }
48873
48874                 // Add warning if user is not logged in
48875                 var hasAuth = osm.authenticated();
48876                 var authWarning = detailSection.selectAll('.auth-warning')
48877                     .data(hasAuth ? [] : [0]);
48878
48879                 authWarning.exit()
48880                     .transition()
48881                     .duration(200)
48882                     .style('opacity', 0)
48883                     .remove();
48884
48885                 var authEnter = authWarning.enter()
48886                     .insert('div', '.tag-reference-body')
48887                     .attr('class', 'field-warning auth-warning')
48888                     .style('opacity', 0);
48889
48890                 authEnter
48891                     .call(svgIcon('#iD-icon-alert', 'inline'));
48892
48893                 authEnter
48894                     .append('span')
48895                     .text(_t('note.login'));
48896
48897                 authEnter
48898                     .append('a')
48899                     .attr('target', '_blank')
48900                     .call(svgIcon('#iD-icon-out-link', 'inline'))
48901                     .append('span')
48902                     .text(_t('login'))
48903                     .on('click.note-login', function() {
48904                         event.preventDefault();
48905                         osm.authenticate();
48906                     });
48907
48908                 authEnter
48909                     .transition()
48910                     .duration(200)
48911                     .style('opacity', 1);
48912
48913
48914                 var prose = detailSection.selectAll('.note-save-prose')
48915                     .data(hasAuth ? [0] : []);
48916
48917                 prose.exit()
48918                     .remove();
48919
48920                 prose = prose.enter()
48921                     .append('p')
48922                     .attr('class', 'note-save-prose')
48923                     .text(_t('note.upload_explanation'))
48924                     .merge(prose);
48925
48926                 osm.userDetails(function(err, user) {
48927                     if (err) { return; }
48928
48929                     var userLink = select(document.createElement('div'));
48930
48931                     if (user.image_url) {
48932                         userLink
48933                             .append('img')
48934                             .attr('src', user.image_url)
48935                             .attr('class', 'icon pre-text user-icon');
48936                     }
48937
48938                     userLink
48939                         .append('a')
48940                         .attr('class', 'user-info')
48941                         .text(user.display_name)
48942                         .attr('href', osm.userURL(user.display_name))
48943                         .attr('tabindex', -1)
48944                         .attr('target', '_blank');
48945
48946                     prose
48947                         .html(_t('note.upload_explanation_with_user', { user: userLink.html() }));
48948                 });
48949             }
48950
48951
48952             function noteSaveButtons(selection) {
48953                 var osm = services.osm;
48954                 var hasAuth = osm && osm.authenticated();
48955
48956                 var isSelected = (_note && _note.id === context.selectedNoteID());
48957                 var buttonSection = selection.selectAll('.buttons')
48958                     .data((isSelected ? [_note] : []), function(d) { return d.status + d.id; });
48959
48960                 // exit
48961                 buttonSection.exit()
48962                     .remove();
48963
48964                 // enter
48965                 var buttonEnter = buttonSection.enter()
48966                     .append('div')
48967                     .attr('class', 'buttons');
48968
48969                 if (_note.isNew()) {
48970                     buttonEnter
48971                         .append('button')
48972                         .attr('class', 'button cancel-button secondary-action')
48973                         .text(_t('confirm.cancel'));
48974
48975                     buttonEnter
48976                         .append('button')
48977                         .attr('class', 'button save-button action')
48978                         .text(_t('note.save'));
48979
48980                 } else {
48981                     buttonEnter
48982                         .append('button')
48983                         .attr('class', 'button status-button action');
48984
48985                     buttonEnter
48986                         .append('button')
48987                         .attr('class', 'button comment-button action')
48988                         .text(_t('note.comment'));
48989                 }
48990
48991
48992                 // update
48993                 buttonSection = buttonSection
48994                     .merge(buttonEnter);
48995
48996                 buttonSection.select('.cancel-button')   // select and propagate data
48997                     .on('click.cancel', clickCancel);
48998
48999                 buttonSection.select('.save-button')     // select and propagate data
49000                     .attr('disabled', isSaveDisabled)
49001                     .on('click.save', clickSave);
49002
49003                 buttonSection.select('.status-button')   // select and propagate data
49004                     .attr('disabled', (hasAuth ? null : true))
49005                     .text(function(d) {
49006                         var action = (d.status === 'open' ? 'close' : 'open');
49007                         var andComment = (d.newComment ? '_comment' : '');
49008                         return _t('note.' + action + andComment);
49009                     })
49010                     .on('click.status', clickStatus);
49011
49012                 buttonSection.select('.comment-button')   // select and propagate data
49013                     .attr('disabled', isSaveDisabled)
49014                     .on('click.comment', clickComment);
49015
49016
49017                 function isSaveDisabled(d) {
49018                     return (hasAuth && d.status === 'open' && d.newComment) ? null : true;
49019                 }
49020             }
49021
49022
49023
49024             function clickCancel(d) {
49025                 this.blur();    // avoid keeping focus on the button - #4641
49026                 var osm = services.osm;
49027                 if (osm) {
49028                     osm.removeNote(d);
49029                 }
49030                 context.enter(modeBrowse(context));
49031                 dispatch$1.call('change');
49032             }
49033
49034
49035             function clickSave(d) {
49036                 this.blur();    // avoid keeping focus on the button - #4641
49037                 var osm = services.osm;
49038                 if (osm) {
49039                     osm.postNoteCreate(d, function(err, note) {
49040                         dispatch$1.call('change', note);
49041                     });
49042                 }
49043             }
49044
49045
49046             function clickStatus(d) {
49047                 this.blur();    // avoid keeping focus on the button - #4641
49048                 var osm = services.osm;
49049                 if (osm) {
49050                     var setStatus = (d.status === 'open' ? 'closed' : 'open');
49051                     osm.postNoteUpdate(d, setStatus, function(err, note) {
49052                         dispatch$1.call('change', note);
49053                     });
49054                 }
49055             }
49056
49057             function clickComment(d) {
49058                 this.blur();    // avoid keeping focus on the button - #4641
49059                 var osm = services.osm;
49060                 if (osm) {
49061                     osm.postNoteUpdate(d, d.status, function(err, note) {
49062                         dispatch$1.call('change', note);
49063                     });
49064                 }
49065             }
49066
49067
49068             noteEditor.note = function(val) {
49069                 if (!arguments.length) { return _note; }
49070                 _note = val;
49071                 return noteEditor;
49072             };
49073
49074             noteEditor.newNote = function(val) {
49075                 if (!arguments.length) { return _newNote; }
49076                 _newNote = val;
49077                 return noteEditor;
49078             };
49079
49080
49081             return utilRebind(noteEditor, dispatch$1, 'on');
49082         }
49083
49084         function modeSelectNote(context, selectedNoteID) {
49085             var mode = {
49086                 id: 'select-note',
49087                 button: 'browse'
49088             };
49089
49090             var _keybinding = utilKeybinding('select-note');
49091             var _noteEditor = uiNoteEditor(context)
49092                 .on('change', function() {
49093                     context.map().pan([0,0]);  // trigger a redraw
49094                     var note = checkSelectedID();
49095                     if (!note) { return; }
49096                     context.ui().sidebar
49097                         .show(_noteEditor.note(note));
49098                 });
49099
49100             var _behaviors = [
49101                 behaviorBreathe(),
49102                 behaviorHover(context),
49103                 behaviorSelect(context),
49104                 behaviorLasso(context),
49105                 modeDragNode(context).behavior,
49106                 modeDragNote(context).behavior
49107             ];
49108
49109             var _newFeature = false;
49110
49111
49112             function checkSelectedID() {
49113                 if (!services.osm) { return; }
49114                 var note = services.osm.getNote(selectedNoteID);
49115                 if (!note) {
49116                     context.enter(modeBrowse(context));
49117                 }
49118                 return note;
49119             }
49120
49121
49122             // class the note as selected, or return to browse mode if the note is gone
49123             function selectNote(drawn) {
49124                 if (!checkSelectedID()) { return; }
49125
49126                 var selection = context.surface().selectAll('.layer-notes .note-' + selectedNoteID);
49127
49128                 if (selection.empty()) {
49129                     // Return to browse mode if selected DOM elements have
49130                     // disappeared because the user moved them out of view..
49131                     var source = event && event.type === 'zoom' && event.sourceEvent;
49132                     if (drawn && source && (source.type === 'pointermove' || source.type === 'mousemove' || source.type === 'touchmove')) {
49133                         context.enter(modeBrowse(context));
49134                     }
49135
49136                 } else {
49137                     selection
49138                         .classed('selected', true);
49139
49140                     context.selectedNoteID(selectedNoteID);
49141                 }
49142             }
49143
49144
49145             function esc() {
49146                 if (context.container().select('.combobox').size()) { return; }
49147                 context.enter(modeBrowse(context));
49148             }
49149
49150
49151             mode.zoomToSelected = function() {
49152                 if (!services.osm) { return; }
49153                 var note = services.osm.getNote(selectedNoteID);
49154                 if (note) {
49155                     context.map().centerZoomEase(note.loc, 20);
49156                 }
49157             };
49158
49159
49160             mode.newFeature = function(val) {
49161                 if (!arguments.length) { return _newFeature; }
49162                 _newFeature = val;
49163                 return mode;
49164             };
49165
49166
49167             mode.enter = function() {
49168                 var note = checkSelectedID();
49169                 if (!note) { return; }
49170
49171                 _behaviors.forEach(context.install);
49172
49173                 _keybinding
49174                     .on(_t('inspector.zoom_to.key'), mode.zoomToSelected)
49175                     .on('⎋', esc, true);
49176
49177                 select(document)
49178                     .call(_keybinding);
49179
49180                 selectNote();
49181
49182                 var sidebar = context.ui().sidebar;
49183                 sidebar.show(_noteEditor.note(note).newNote(_newFeature));
49184
49185                 // expand the sidebar, avoid obscuring the note if needed
49186                 sidebar.expand(sidebar.intersects(note.extent()));
49187
49188                 context.map()
49189                     .on('drawn.select', selectNote);
49190             };
49191
49192
49193             mode.exit = function() {
49194                 _behaviors.forEach(context.uninstall);
49195
49196                 select(document)
49197                     .call(_keybinding.unbind);
49198
49199                 context.surface()
49200                     .selectAll('.layer-notes .selected')
49201                     .classed('selected hover', false);
49202
49203                 context.map()
49204                     .on('drawn.select', null);
49205
49206                 context.ui().sidebar
49207                     .hide();
49208
49209                 context.selectedNoteID(null);
49210             };
49211
49212
49213             return mode;
49214         }
49215
49216         function modeDragNote(context) {
49217             var mode = {
49218                 id: 'drag-note',
49219                 button: 'browse'
49220             };
49221
49222             var edit = behaviorEdit(context);
49223
49224             var _nudgeInterval;
49225             var _lastLoc;
49226             var _note;    // most current note.. dragged note may have stale datum.
49227
49228
49229             function startNudge(nudge) {
49230                 if (_nudgeInterval) { window.clearInterval(_nudgeInterval); }
49231                 _nudgeInterval = window.setInterval(function() {
49232                     context.map().pan(nudge);
49233                     doMove(nudge);
49234                 }, 50);
49235             }
49236
49237
49238             function stopNudge() {
49239                 if (_nudgeInterval) {
49240                     window.clearInterval(_nudgeInterval);
49241                     _nudgeInterval = null;
49242                 }
49243             }
49244
49245
49246             function origin(note) {
49247                 return context.projection(note.loc);
49248             }
49249
49250
49251             function start(note) {
49252                 _note = note;
49253                 var osm = services.osm;
49254                 if (osm) {
49255                     // Get latest note from cache.. The marker may have a stale datum bound to it
49256                     // and dragging it around can sometimes delete the users note comment.
49257                     _note = osm.getNote(_note.id);
49258                 }
49259
49260                 context.surface().selectAll('.note-' + _note.id)
49261                     .classed('active', true);
49262
49263                 context.perform(actionNoop());
49264                 context.enter(mode);
49265                 context.selectedNoteID(_note.id);
49266             }
49267
49268
49269             function move() {
49270                 event.sourceEvent.stopPropagation();
49271                 _lastLoc = context.projection.invert(event.point);
49272
49273                 doMove();
49274                 var nudge = geoViewportEdge(event.point, context.map().dimensions());
49275                 if (nudge) {
49276                     startNudge(nudge);
49277                 } else {
49278                     stopNudge();
49279                 }
49280             }
49281
49282
49283             function doMove(nudge) {
49284                 nudge = nudge || [0, 0];
49285
49286                 var currPoint = (event && event.point) || context.projection(_lastLoc);
49287                 var currMouse = geoVecSubtract(currPoint, nudge);
49288                 var loc = context.projection.invert(currMouse);
49289
49290                 _note = _note.move(loc);
49291
49292                 var osm = services.osm;
49293                 if (osm) {
49294                     osm.replaceNote(_note);  // update note cache
49295                 }
49296
49297                 context.replace(actionNoop());   // trigger redraw
49298             }
49299
49300
49301             function end() {
49302                 context.replace(actionNoop());   // trigger redraw
49303
49304                 context
49305                     .selectedNoteID(_note.id)
49306                     .enter(modeSelectNote(context, _note.id));
49307             }
49308
49309
49310             var drag = behaviorDrag()
49311                 .selector('.layer-touch.markers .target.note.new')
49312                 .surface(context.container().select('.main-map').node())
49313                 .origin(origin)
49314                 .on('start', start)
49315                 .on('move', move)
49316                 .on('end', end);
49317
49318
49319             mode.enter = function() {
49320                 context.install(edit);
49321             };
49322
49323
49324             mode.exit = function() {
49325                 context.ui().sidebar.hover.cancel();
49326                 context.uninstall(edit);
49327
49328                 context.surface()
49329                     .selectAll('.active')
49330                     .classed('active', false);
49331
49332                 stopNudge();
49333             };
49334
49335             mode.behavior = drag;
49336
49337             return mode;
49338         }
49339
49340         function uiDataHeader() {
49341             var _datum;
49342
49343
49344             function dataHeader(selection) {
49345                 var header = selection.selectAll('.data-header')
49346                     .data(
49347                         (_datum ? [_datum] : []),
49348                         function(d) { return d.__featurehash__; }
49349                     );
49350
49351                 header.exit()
49352                     .remove();
49353
49354                 var headerEnter = header.enter()
49355                     .append('div')
49356                     .attr('class', 'data-header');
49357
49358                 var iconEnter = headerEnter
49359                     .append('div')
49360                     .attr('class', 'data-header-icon');
49361
49362                 iconEnter
49363                     .append('div')
49364                     .attr('class', 'preset-icon-28')
49365                     .call(svgIcon('#iD-icon-data', 'note-fill'));
49366
49367                 headerEnter
49368                     .append('div')
49369                     .attr('class', 'data-header-label')
49370                     .text(_t('map_data.layers.custom.title'));
49371             }
49372
49373
49374             dataHeader.datum = function(val) {
49375                 if (!arguments.length) { return _datum; }
49376                 _datum = val;
49377                 return this;
49378             };
49379
49380
49381             return dataHeader;
49382         }
49383
49384         // This code assumes that the combobox values will not have duplicate entries.
49385         // It is keyed on the `value` of the entry. Data should be an array of objects like:
49386         //   [{
49387         //       value:  'display text',  // required
49388         //       title:  'hover text'     // optional
49389         //   }, ...]
49390
49391         var _comboHideTimerID;
49392
49393         function uiCombobox(context, klass) {
49394             var dispatch$1 = dispatch('accept', 'cancel');
49395             var container = context.container();
49396
49397             var _suggestions = [];
49398             var _data = [];
49399             var _fetched = {};
49400             var _selected = null;
49401             var _canAutocomplete = true;
49402             var _caseSensitive = false;
49403             var _cancelFetch = false;
49404             var _minItems = 2;
49405             var _tDown = 0;
49406             var _mouseEnterHandler, _mouseLeaveHandler;
49407
49408             var _fetcher = function(val, cb) {
49409                 cb(_data.filter(function(d) {
49410                     var terms = d.terms || [];
49411                     terms.push(d.value);
49412                     return terms.some(function(term) {
49413                         return term
49414                             .toString()
49415                             .toLowerCase()
49416                             .indexOf(val.toLowerCase()) !== -1;
49417                     });
49418                 }));
49419             };
49420
49421             var combobox = function(input, attachTo) {
49422                 if (!input || input.empty()) { return; }
49423
49424                 input
49425                     .classed('combobox-input', true)
49426                     .on('focus.combo-input', focus)
49427                     .on('blur.combo-input', blur)
49428                     .on('keydown.combo-input', keydown)
49429                     .on('keyup.combo-input', keyup)
49430                     .on('input.combo-input', change)
49431                     .on('mousedown.combo-input', mousedown)
49432                     .each(function() {
49433                         var parent = this.parentNode;
49434                         var sibling = this.nextSibling;
49435
49436                         select(parent).selectAll('.combobox-caret')
49437                             .filter(function(d) { return d === input.node(); })
49438                             .data([input.node()])
49439                             .enter()
49440                             .insert('div', function() { return sibling; })
49441                             .attr('class', 'combobox-caret')
49442                             .on('mousedown.combo-caret', function() {
49443                                 event.preventDefault(); // don't steal focus from input
49444                                 input.node().focus(); // focus the input as if it was clicked
49445                                 mousedown();
49446                             })
49447                             .on('mouseup.combo-caret', function() {
49448                                 event.preventDefault(); // don't steal focus from input
49449                                 mouseup();
49450                             });
49451                     });
49452
49453
49454                 function mousedown() {
49455                     if (event.button !== 0) { return; }    // left click only
49456                     _tDown = +new Date();
49457
49458                     // clear selection
49459                     var start = input.property('selectionStart');
49460                     var end = input.property('selectionEnd');
49461                     if (start !== end) {
49462                         var val = utilGetSetValue(input);
49463                         input.node().setSelectionRange(val.length, val.length);
49464                         return;
49465                     }
49466
49467                     input.on('mouseup.combo-input', mouseup);
49468                 }
49469
49470
49471                 function mouseup() {
49472                     input.on('mouseup.combo-input', null);
49473                     if (event.button !== 0) { return; }    // left click only
49474                     if (input.node() !== document.activeElement) { return; }   // exit if this input is not focused
49475
49476                     var start = input.property('selectionStart');
49477                     var end = input.property('selectionEnd');
49478                     if (start !== end) { return; }  // exit if user is selecting
49479
49480                     // not showing or showing for a different field - try to show it.
49481                     var combo = container.selectAll('.combobox');
49482                     if (combo.empty() || combo.datum() !== input.node()) {
49483                         var tOrig = _tDown;
49484                         window.setTimeout(function() {
49485                             if (tOrig !== _tDown) { return; }   // exit if user double clicked
49486                             fetchComboData('', function() {
49487                                 show();
49488                                 render();
49489                             });
49490                         }, 250);
49491
49492                     } else {
49493                         hide();
49494                     }
49495                 }
49496
49497
49498                 function focus() {
49499                     fetchComboData('');   // prefetch values (may warm taginfo cache)
49500                 }
49501
49502
49503                 function blur() {
49504                     _comboHideTimerID = window.setTimeout(hide, 75);
49505                 }
49506
49507
49508                 function show() {
49509                     hide();   // remove any existing
49510
49511                     container
49512                         .insert('div', ':first-child')
49513                         .datum(input.node())
49514                         .attr('class', 'combobox' + (klass ? ' combobox-' + klass : ''))
49515                         .style('position', 'absolute')
49516                         .style('display', 'block')
49517                         .style('left', '0px')
49518                         .on('mousedown.combo-container', function () {
49519                             // prevent moving focus out of the input field
49520                             event.preventDefault();
49521                         });
49522
49523                     container
49524                         .on('scroll.combo-scroll', render, true);
49525                 }
49526
49527
49528                 function hide() {
49529                     if (_comboHideTimerID) {
49530                         window.clearTimeout(_comboHideTimerID);
49531                         _comboHideTimerID = undefined;
49532                     }
49533
49534                     container.selectAll('.combobox')
49535                         .remove();
49536
49537                     container
49538                         .on('scroll.combo-scroll', null);
49539                 }
49540
49541
49542                 function keydown() {
49543                     var shown = !container.selectAll('.combobox').empty();
49544                     var tagName = input.node() ? input.node().tagName.toLowerCase() : '';
49545
49546                     switch (event.keyCode) {
49547                         case 8:   // ⌫ Backspace
49548                         case 46:  // ⌦ Delete
49549                             event.stopPropagation();
49550                             _selected = null;
49551                             render();
49552                             input.on('input.combo-input', function() {
49553                                 var start = input.property('selectionStart');
49554                                 input.node().setSelectionRange(start, start);
49555                                 input.on('input.combo-input', change);
49556                             });
49557                             break;
49558
49559                         case 9:   // ⇥ Tab
49560                             accept();
49561                             break;
49562
49563                         case 13:  // ↩ Return
49564                             event.preventDefault();
49565                             event.stopPropagation();
49566                             break;
49567
49568                         case 38:  // ↑ Up arrow
49569                             if (tagName === 'textarea' && !shown) { return; }
49570                             event.preventDefault();
49571                             if (tagName === 'input' && !shown) {
49572                                 show();
49573                             }
49574                             nav(-1);
49575                             break;
49576
49577                         case 40:  // ↓ Down arrow
49578                             if (tagName === 'textarea' && !shown) { return; }
49579                             event.preventDefault();
49580                             if (tagName === 'input' && !shown) {
49581                                 show();
49582                             }
49583                             nav(+1);
49584                             break;
49585                     }
49586                 }
49587
49588
49589                 function keyup() {
49590                     switch (event.keyCode) {
49591                         case 27:  // ⎋ Escape
49592                             cancel();
49593                             break;
49594
49595                         case 13:  // ↩ Return
49596                             accept();
49597                             break;
49598                     }
49599                 }
49600
49601
49602                 // Called whenever the input value is changed (e.g. on typing)
49603                 function change() {
49604                     fetchComboData(value(), function() {
49605                         _selected = null;
49606                         var val = input.property('value');
49607
49608                         if (_suggestions.length) {
49609                             if (input.property('selectionEnd') === val.length) {
49610                                 _selected = tryAutocomplete();
49611                             }
49612
49613                             if (!_selected) {
49614                                 _selected = val;
49615                             }
49616                         }
49617
49618                         if (val.length) {
49619                             var combo = container.selectAll('.combobox');
49620                             if (combo.empty()) {
49621                                 show();
49622                             }
49623                         } else {
49624                             hide();
49625                         }
49626
49627                         render();
49628                     });
49629                 }
49630
49631
49632                 // Called when the user presses up/down arrows to navigate the list
49633                 function nav(dir) {
49634                     if (_suggestions.length) {
49635                         // try to determine previously selected index..
49636                         var index = -1;
49637                         for (var i = 0; i < _suggestions.length; i++) {
49638                             if (_selected && _suggestions[i].value === _selected) {
49639                                 index = i;
49640                                 break;
49641                             }
49642                         }
49643
49644                         // pick new _selected
49645                         index = Math.max(Math.min(index + dir, _suggestions.length - 1), 0);
49646                         _selected = _suggestions[index].value;
49647                         input.property('value', _selected);
49648                     }
49649
49650                     render();
49651                     ensureVisible();
49652                 }
49653
49654
49655                 function ensureVisible() {
49656                     var combo = container.selectAll('.combobox');
49657                     if (combo.empty()) { return; }
49658
49659                     var containerRect = container.node().getBoundingClientRect();
49660                     var comboRect = combo.node().getBoundingClientRect();
49661
49662                     if (comboRect.bottom > containerRect.bottom) {
49663                         var node = attachTo ? attachTo.node() : input.node();
49664                         node.scrollIntoView({ behavior: 'instant', block: 'center' });
49665                         render();
49666                     }
49667
49668                     // https://stackoverflow.com/questions/11039885/scrollintoview-causing-the-whole-page-to-move
49669                     var selected = combo.selectAll('.combobox-option.selected').node();
49670                     if (selected) {
49671                         selected.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
49672                     }
49673                 }
49674
49675
49676                 function value() {
49677                     var value = input.property('value');
49678                     var start = input.property('selectionStart');
49679                     var end = input.property('selectionEnd');
49680
49681                     if (start && end) {
49682                         value = value.substring(0, start);
49683                     }
49684
49685                     return value;
49686                 }
49687
49688
49689                 function fetchComboData(v, cb) {
49690                     _cancelFetch = false;
49691
49692                     _fetcher.call(input, v, function(results) {
49693                         // already chose a value, don't overwrite or autocomplete it
49694                         if (_cancelFetch) { return; }
49695
49696                         _suggestions = results;
49697                         results.forEach(function(d) { _fetched[d.value] = d; });
49698
49699                         if (cb) {
49700                             cb();
49701                         }
49702                     });
49703                 }
49704
49705
49706                 function tryAutocomplete() {
49707                     if (!_canAutocomplete) { return; }
49708
49709                     var val = _caseSensitive ? value() : value().toLowerCase();
49710                     if (!val) { return; }
49711
49712                     // Don't autocomplete if user is typing a number - #4935
49713                     if (!isNaN(parseFloat(val)) && isFinite(val)) { return; }
49714
49715                     var bestIndex = -1;
49716                     for (var i = 0; i < _suggestions.length; i++) {
49717                         var suggestion = _suggestions[i].value;
49718                         var compare = _caseSensitive ? suggestion : suggestion.toLowerCase();
49719
49720                         // if search string matches suggestion exactly, pick it..
49721                         if (compare === val) {
49722                             bestIndex = i;
49723                             break;
49724
49725                         // otherwise lock in the first result that starts with the search string..
49726                         } else if (bestIndex === -1 && compare.indexOf(val) === 0) {
49727                             bestIndex = i;
49728                         }
49729                     }
49730
49731                     if (bestIndex !== -1) {
49732                         var bestVal = _suggestions[bestIndex].value;
49733                         input.property('value', bestVal);
49734                         input.node().setSelectionRange(val.length, bestVal.length);
49735                         return bestVal;
49736                     }
49737                 }
49738
49739
49740                 function render() {
49741                     if (_suggestions.length < _minItems || document.activeElement !== input.node()) {
49742                         hide();
49743                         return;
49744                     }
49745
49746                     var shown = !container.selectAll('.combobox').empty();
49747                     if (!shown) { return; }
49748
49749                     var combo = container.selectAll('.combobox');
49750                     var options = combo.selectAll('.combobox-option')
49751                         .data(_suggestions, function(d) { return d.value; });
49752
49753                     options.exit()
49754                         .remove();
49755
49756                     // enter/update
49757                     options.enter()
49758                         .append('a')
49759                         .attr('class', 'combobox-option')
49760                         .attr('title', function(d) { return d.title; })
49761                         .text(function(d) { return d.display || d.value; })
49762                         .on('mouseenter', _mouseEnterHandler)
49763                         .on('mouseleave', _mouseLeaveHandler)
49764                         .merge(options)
49765                         .classed('selected', function(d) { return d.value === _selected; })
49766                         .on('click.combo-option', accept)
49767                         .order();
49768
49769                     var node = attachTo ? attachTo.node() : input.node();
49770                     var containerRect = container.node().getBoundingClientRect();
49771                     var rect = node.getBoundingClientRect();
49772
49773                     combo
49774                         .style('left', (rect.left + 5 - containerRect.left) + 'px')
49775                         .style('width', (rect.width - 10) + 'px')
49776                         .style('top', (rect.height + rect.top - containerRect.top) + 'px');
49777                 }
49778
49779
49780                 // Dispatches an 'accept' event
49781                 // Then hides the combobox.
49782                 function accept(d) {
49783                     _cancelFetch = true;
49784                     var thiz = input.node();
49785
49786                     if (d) {   // user clicked on a suggestion
49787                         utilGetSetValue(input, d.value);    // replace field contents
49788                         utilTriggerEvent(input, 'change');
49789                     }
49790
49791                     // clear (and keep) selection
49792                     var val = utilGetSetValue(input);
49793                     thiz.setSelectionRange(val.length, val.length);
49794
49795                     d = _fetched[val];
49796                     dispatch$1.call('accept', thiz, d, val);
49797                     hide();
49798                 }
49799
49800
49801                 // Dispatches an 'cancel' event
49802                 // Then hides the combobox.
49803                 function cancel() {
49804                     _cancelFetch = true;
49805                     var thiz = input.node();
49806
49807                     // clear (and remove) selection, and replace field contents
49808                     var val = utilGetSetValue(input);
49809                     var start = input.property('selectionStart');
49810                     var end = input.property('selectionEnd');
49811                     val = val.slice(0, start) + val.slice(end);
49812                     utilGetSetValue(input, val);
49813                     thiz.setSelectionRange(val.length, val.length);
49814
49815                     dispatch$1.call('cancel', thiz);
49816                     hide();
49817                 }
49818
49819             };
49820
49821
49822             combobox.canAutocomplete = function(val) {
49823                 if (!arguments.length) { return _canAutocomplete; }
49824                 _canAutocomplete = val;
49825                 return combobox;
49826             };
49827
49828             combobox.caseSensitive = function(val) {
49829                 if (!arguments.length) { return _caseSensitive; }
49830                 _caseSensitive = val;
49831                 return combobox;
49832             };
49833
49834             combobox.data = function(val) {
49835                 if (!arguments.length) { return _data; }
49836                 _data = val;
49837                 return combobox;
49838             };
49839
49840             combobox.fetcher = function(val) {
49841                 if (!arguments.length) { return _fetcher; }
49842                 _fetcher = val;
49843                 return combobox;
49844             };
49845
49846             combobox.minItems = function(val) {
49847                 if (!arguments.length) { return _minItems; }
49848                 _minItems = val;
49849                 return combobox;
49850             };
49851
49852             combobox.itemsMouseEnter = function(val) {
49853                 if (!arguments.length) { return _mouseEnterHandler; }
49854                 _mouseEnterHandler = val;
49855                 return combobox;
49856             };
49857
49858             combobox.itemsMouseLeave = function(val) {
49859                 if (!arguments.length) { return _mouseLeaveHandler; }
49860                 _mouseLeaveHandler = val;
49861                 return combobox;
49862             };
49863
49864             return utilRebind(combobox, dispatch$1, 'on');
49865         }
49866
49867
49868         uiCombobox.off = function(input, context) {
49869             input
49870                 .on('focus.combo-input', null)
49871                 .on('blur.combo-input', null)
49872                 .on('keydown.combo-input', null)
49873                 .on('keyup.combo-input', null)
49874                 .on('input.combo-input', null)
49875                 .on('mousedown.combo-input', null)
49876                 .on('mouseup.combo-input', null);
49877
49878
49879             context.container()
49880                 .on('scroll.combo-scroll', null);
49881         };
49882
49883         // toggles the visibility of ui elements, using a combination of the
49884         // hide class, which sets display=none, and a d3 transition for opacity.
49885         // this will cause blinking when called repeatedly, so check that the
49886         // value actually changes between calls.
49887         function uiToggle(show, callback) {
49888             return function(selection) {
49889                 selection
49890                     .style('opacity', show ? 0 : 1)
49891                     .classed('hide', false)
49892                     .transition()
49893                     .style('opacity', show ? 1 : 0)
49894                     .on('end', function() {
49895                         select(this)
49896                             .classed('hide', !show)
49897                             .style('opacity', null);
49898                         if (callback) { callback.apply(this); }
49899                     });
49900             };
49901         }
49902
49903         function uiDisclosure(context, key, expandedDefault) {
49904             var dispatch$1 = dispatch('toggled');
49905             var _expanded;
49906             var _title = utilFunctor('');
49907             var _updatePreference = true;
49908             var _content = function () {};
49909
49910
49911             var disclosure = function(selection) {
49912
49913                 if (_expanded === undefined || _expanded === null) {
49914                     // loading _expanded here allows it to be reset by calling `disclosure.expanded(null)`
49915
49916                     var preference = corePreferences('disclosure.' + key + '.expanded');
49917                     _expanded = preference === null ? !!expandedDefault : (preference === 'true');
49918                 }
49919
49920                 var hideToggle = selection.selectAll('.hide-toggle-' + key)
49921                     .data([0]);
49922
49923                 // enter
49924                 var hideToggleEnter = hideToggle.enter()
49925                     .append('a')
49926                     .attr('href', '#')
49927                     .attr('class', 'hide-toggle hide-toggle-' + key)
49928                     .call(svgIcon('', 'pre-text', 'hide-toggle-icon'));
49929
49930                 hideToggleEnter
49931                     .append('span')
49932                     .attr('class', 'hide-toggle-text');
49933
49934                 // update
49935                 hideToggle = hideToggleEnter
49936                     .merge(hideToggle);
49937
49938                 hideToggle
49939                     .on('click', toggle)
49940                     .classed('expanded', _expanded);
49941
49942                 hideToggle.selectAll('.hide-toggle-text')
49943                     .text(_title());
49944
49945                 hideToggle.selectAll('.hide-toggle-icon')
49946                     .attr('xlink:href', _expanded ? '#iD-icon-down'
49947                         : (_mainLocalizer.textDirection() === 'rtl') ? '#iD-icon-backward' : '#iD-icon-forward'
49948                     );
49949
49950
49951                 var wrap = selection.selectAll('.disclosure-wrap')
49952                     .data([0]);
49953
49954                 // enter/update
49955                 wrap = wrap.enter()
49956                     .append('div')
49957                     .attr('class', 'disclosure-wrap disclosure-wrap-' + key)
49958                     .merge(wrap)
49959                     .classed('hide', !_expanded);
49960
49961                 if (_expanded) {
49962                     wrap
49963                         .call(_content);
49964                 }
49965
49966
49967                 function toggle() {
49968                     event.preventDefault();
49969
49970                     _expanded = !_expanded;
49971
49972                     if (_updatePreference) {
49973                         corePreferences('disclosure.' + key + '.expanded', _expanded);
49974                     }
49975
49976                     hideToggle
49977                         .classed('expanded', _expanded);
49978
49979                     hideToggle.selectAll('.hide-toggle-icon')
49980                         .attr('xlink:href', _expanded ? '#iD-icon-down'
49981                             : (_mainLocalizer.textDirection() === 'rtl') ? '#iD-icon-backward' : '#iD-icon-forward'
49982                         );
49983
49984                     wrap
49985                         .call(uiToggle(_expanded));
49986
49987                     if (_expanded) {
49988                         wrap
49989                             .call(_content);
49990                     }
49991
49992                     dispatch$1.call('toggled', this, _expanded);
49993                 }
49994             };
49995
49996
49997             disclosure.title = function(val) {
49998                 if (!arguments.length) { return _title; }
49999                 _title = utilFunctor(val);
50000                 return disclosure;
50001             };
50002
50003
50004             disclosure.expanded = function(val) {
50005                 if (!arguments.length) { return _expanded; }
50006                 _expanded = val;
50007                 return disclosure;
50008             };
50009
50010
50011             disclosure.updatePreference = function(val) {
50012                 if (!arguments.length) { return _updatePreference; }
50013                 _updatePreference = val;
50014                 return disclosure;
50015             };
50016
50017
50018             disclosure.content = function(val) {
50019                 if (!arguments.length) { return _content; }
50020                 _content = val;
50021                 return disclosure;
50022             };
50023
50024
50025             return utilRebind(disclosure, dispatch$1, 'on');
50026         }
50027
50028         // A unit of controls or info to be used in a layout, such as within a pane.
50029         // Can be labeled and collapsible.
50030         function uiSection(id, context) {
50031
50032             var _classes = utilFunctor('');
50033             var _shouldDisplay;
50034             var _content;
50035
50036             var _disclosure;
50037             var _title;
50038             var _expandedByDefault = utilFunctor(true);
50039             var _disclosureContent;
50040             var _disclosureExpanded;
50041
50042             var _containerSelection = select(null);
50043
50044             var section = {
50045                 id: id
50046             };
50047
50048             section.classes = function(val) {
50049                 if (!arguments.length) { return _classes; }
50050                 _classes = utilFunctor(val);
50051                 return section;
50052             };
50053
50054             section.title = function(val) {
50055                 if (!arguments.length) { return _title; }
50056                 _title = utilFunctor(val);
50057                 return section;
50058             };
50059
50060             section.expandedByDefault = function(val) {
50061                 if (!arguments.length) { return _expandedByDefault; }
50062                 _expandedByDefault = utilFunctor(val);
50063                 return section;
50064             };
50065
50066             section.shouldDisplay = function(val) {
50067                 if (!arguments.length) { return _shouldDisplay; }
50068                 _shouldDisplay = utilFunctor(val);
50069                 return section;
50070             };
50071
50072             section.content = function(val) {
50073                 if (!arguments.length) { return _content; }
50074                 _content = val;
50075                 return section;
50076             };
50077
50078             section.disclosureContent = function(val) {
50079                 if (!arguments.length) { return _disclosureContent; }
50080                 _disclosureContent = val;
50081                 return section;
50082             };
50083
50084             section.disclosureExpanded = function(val) {
50085                 if (!arguments.length) { return _disclosureExpanded; }
50086                 _disclosureExpanded = val;
50087                 return section;
50088             };
50089
50090             // may be called multiple times
50091             section.render = function(selection) {
50092
50093                 _containerSelection = selection
50094                     .selectAll('.section-' + id)
50095                     .data([0]);
50096
50097                 var sectionEnter = _containerSelection
50098                     .enter()
50099                     .append('div')
50100                     .attr('class', 'section section-' + id + ' ' + (_classes && _classes() || ''));
50101
50102                 _containerSelection = sectionEnter
50103                     .merge(_containerSelection);
50104
50105                 _containerSelection
50106                     .call(renderContent);
50107             };
50108
50109             section.reRender = function() {
50110                 _containerSelection
50111                     .call(renderContent);
50112             };
50113
50114             section.selection = function() {
50115                 return _containerSelection;
50116             };
50117
50118             section.disclosure = function() {
50119                 return _disclosure;
50120             };
50121
50122             // may be called multiple times
50123             function renderContent(selection) {
50124                 if (_shouldDisplay) {
50125                     var shouldDisplay = _shouldDisplay();
50126                     selection.classed('hide', !shouldDisplay);
50127                     if (!shouldDisplay) {
50128                         selection.html('');
50129                         return;
50130                     }
50131                 }
50132
50133                 if (_disclosureContent) {
50134                     if (!_disclosure) {
50135                         _disclosure = uiDisclosure(context, id.replace(/-/g, '_'), _expandedByDefault())
50136                             .title(_title || '')
50137                             /*.on('toggled', function(expanded) {
50138                                 if (expanded) { selection.node().parentNode.scrollTop += 200; }
50139                             })*/
50140                             .content(_disclosureContent);
50141                     }
50142                     if (_disclosureExpanded !== undefined) {
50143                         _disclosure.expanded(_disclosureExpanded);
50144                         _disclosureExpanded = undefined;
50145                     }
50146                     selection
50147                         .call(_disclosure);
50148
50149                     return;
50150                 }
50151
50152                 if (_content) {
50153                     selection
50154                         .call(_content);
50155                 }
50156             }
50157
50158             return section;
50159         }
50160
50161         // Pass `what` object of the form:
50162         // {
50163         //   key: 'string',     // required
50164         //   value: 'string'    // optional
50165         // }
50166         //   -or-
50167         // {
50168         //   qid: 'string'      // brand wikidata  (e.g. 'Q37158')
50169         // }
50170         //
50171         function uiTagReference(what) {
50172             var wikibase = what.qid ? services.wikidata : services.osmWikibase;
50173             var tagReference = {};
50174
50175             var _button = select(null);
50176             var _body = select(null);
50177             var _loaded;
50178             var _showing;
50179
50180
50181             function load() {
50182                 if (!wikibase) { return; }
50183
50184                 _button
50185                     .classed('tag-reference-loading', true);
50186
50187                 wikibase.getDocs(what, gotDocs);
50188             }
50189
50190
50191             function gotDocs(err, docs) {
50192                 _body.html('');
50193
50194                 if (!docs || !docs.title) {
50195                     _body
50196                         .append('p')
50197                         .attr('class', 'tag-reference-description')
50198                         .text(_t('inspector.no_documentation_key'));
50199                     done();
50200                     return;
50201                 }
50202
50203                 if (docs.imageURL) {
50204                     _body
50205                         .append('img')
50206                         .attr('class', 'tag-reference-wiki-image')
50207                         .attr('src', docs.imageURL)
50208                         .on('load', function() { done(); })
50209                         .on('error', function() { select(this).remove(); done(); });
50210                 } else {
50211                     done();
50212                 }
50213
50214                 _body
50215                     .append('p')
50216                     .attr('class', 'tag-reference-description')
50217                     .text(docs.description || _t('inspector.no_documentation_key'))
50218                     .append('a')
50219                     .attr('class', 'tag-reference-edit')
50220                     .attr('target', '_blank')
50221                     .attr('tabindex', -1)
50222                     .attr('title', _t('inspector.edit_reference'))
50223                     .attr('href', docs.editURL)
50224                     .call(svgIcon('#iD-icon-edit', 'inline'));
50225
50226                 if (docs.wiki) {
50227                     _body
50228                       .append('a')
50229                       .attr('class', 'tag-reference-link')
50230                       .attr('target', '_blank')
50231                       .attr('tabindex', -1)
50232                       .attr('href', docs.wiki.url)
50233                       .call(svgIcon('#iD-icon-out-link', 'inline'))
50234                       .append('span')
50235                       .text(_t(docs.wiki.text));
50236                 }
50237
50238                 // Add link to info about "good changeset comments" - #2923
50239                 if (what.key === 'comment') {
50240                     _body
50241                         .append('a')
50242                         .attr('class', 'tag-reference-comment-link')
50243                         .attr('target', '_blank')
50244                         .attr('tabindex', -1)
50245                         .call(svgIcon('#iD-icon-out-link', 'inline'))
50246                         .attr('href', _t('commit.about_changeset_comments_link'))
50247                         .append('span')
50248                         .text(_t('commit.about_changeset_comments'));
50249                 }
50250             }
50251
50252
50253             function done() {
50254                 _loaded = true;
50255
50256                 _button
50257                     .classed('tag-reference-loading', false);
50258
50259                 _body
50260                     .classed('expanded', true)
50261                     .transition()
50262                     .duration(200)
50263                     .style('max-height', '200px')
50264                     .style('opacity', '1');
50265
50266                 _showing = true;
50267
50268                 _button.selectAll('svg.icon use').each(function() {
50269                     var iconUse = select(this);
50270                     if (iconUse.attr('href') === '#iD-icon-info') {
50271                         iconUse.attr('href', '#iD-icon-info-filled');
50272                     }
50273                 });
50274             }
50275
50276
50277             function hide() {
50278                 _body
50279                     .transition()
50280                     .duration(200)
50281                     .style('max-height', '0px')
50282                     .style('opacity', '0')
50283                     .on('end', function () {
50284                         _body.classed('expanded', false);
50285                     });
50286
50287                 _showing = false;
50288
50289                 _button.selectAll('svg.icon use').each(function() {
50290                     var iconUse = select(this);
50291                     if (iconUse.attr('href') === '#iD-icon-info-filled') {
50292                         iconUse.attr('href', '#iD-icon-info');
50293                     }
50294                 });
50295
50296             }
50297
50298
50299             tagReference.button = function(selection, klass, iconName) {
50300                 _button = selection.selectAll('.tag-reference-button')
50301                     .data([0]);
50302
50303                 _button = _button.enter()
50304                     .append('button')
50305                     .attr('class', 'tag-reference-button ' + klass)
50306                     .attr('title', _t('icons.information'))
50307                     .attr('tabindex', -1)
50308                     .call(svgIcon('#iD-icon-' + (iconName || 'inspect')))
50309                     .merge(_button);
50310
50311                 _button
50312                     .on('click', function () {
50313                         event.stopPropagation();
50314                         event.preventDefault();
50315                         this.blur();    // avoid keeping focus on the button - #4641
50316                         if (_showing) {
50317                             hide();
50318                         } else if (_loaded) {
50319                             done();
50320                         } else {
50321                             load();
50322                         }
50323                     });
50324             };
50325
50326
50327             tagReference.body = function(selection) {
50328                 var itemID = what.qid || (what.key + '-' + (what.value || ''));
50329                 _body = selection.selectAll('.tag-reference-body')
50330                     .data([itemID], function(d) { return d; });
50331
50332                 _body.exit()
50333                     .remove();
50334
50335                 _body = _body.enter()
50336                     .append('div')
50337                     .attr('class', 'tag-reference-body')
50338                     .style('max-height', '0')
50339                     .style('opacity', '0')
50340                     .merge(_body);
50341
50342                 if (_showing === false) {
50343                     hide();
50344                 }
50345             };
50346
50347
50348             tagReference.showing = function(val) {
50349                 if (!arguments.length) { return _showing; }
50350                 _showing = val;
50351                 return tagReference;
50352             };
50353
50354
50355             return tagReference;
50356         }
50357
50358         function uiSectionRawTagEditor(id, context) {
50359
50360             var section = uiSection(id, context)
50361                 .classes('raw-tag-editor')
50362                 .title(function() {
50363                     var count = Object.keys(_tags).filter(function(d) { return d; }).length;
50364                     return _t('inspector.title_count', { title: _t('inspector.tags'), count: count });
50365                 })
50366                 .expandedByDefault(false)
50367                 .disclosureContent(renderDisclosureContent);
50368
50369             var taginfo = services.taginfo;
50370             var dispatch$1 = dispatch('change');
50371             var availableViews = [
50372                 { id: 'text', icon: '#fas-i-cursor' },
50373                 { id: 'list', icon: '#fas-th-list' }
50374             ];
50375
50376             var _tagView = (corePreferences('raw-tag-editor-view') || 'list');   // 'list, 'text'
50377             var _readOnlyTags = [];
50378             // the keys in the order we want them to display
50379             var _orderedKeys = [];
50380             var _showBlank = false;
50381             var _pendingChange = null;
50382             var _state;
50383             var _presets;
50384             var _tags;
50385             var _entityIDs;
50386
50387             function renderDisclosureContent(wrap) {
50388
50389                 // remove deleted keys
50390                 _orderedKeys = _orderedKeys.filter(function(key) {
50391                     return _tags[key] !== undefined;
50392                 });
50393
50394                 // When switching to a different entity or changing the state (hover/select)
50395                 // reorder the keys alphabetically.
50396                 // We trigger this by emptying the `_orderedKeys` array, then it will be rebuilt here.
50397                 // Otherwise leave their order alone - #5857, #5927
50398                 var all = Object.keys(_tags).sort();
50399                 var missingKeys = utilArrayDifference(all, _orderedKeys);
50400                 for (var i in missingKeys) {
50401                     _orderedKeys.push(missingKeys[i]);
50402                 }
50403
50404                 // assemble row data
50405                 var rowData = _orderedKeys.map(function(key, i) {
50406                     return { index: i, key: key, value: _tags[key] };
50407                 });
50408
50409                 // append blank row last, if necessary
50410                 if (!rowData.length || _showBlank) {
50411                     _showBlank = false;
50412                     rowData.push({ index: rowData.length, key: '', value: '' });
50413                 }
50414
50415
50416                 // View Options
50417                 var options = wrap.selectAll('.raw-tag-options')
50418                     .data([0]);
50419
50420                 options.exit()
50421                     .remove();
50422
50423                 var optionsEnter = options.enter()
50424                     .insert('div', ':first-child')
50425                     .attr('class', 'raw-tag-options');
50426
50427                 var optionEnter = optionsEnter.selectAll('.raw-tag-option')
50428                     .data(availableViews, function(d) { return d.id; })
50429                     .enter();
50430
50431                 optionEnter
50432                     .append('button')
50433                     .attr('class', function(d) {
50434                         return 'raw-tag-option raw-tag-option-' + d.id + (_tagView === d.id ? ' selected' : '');
50435                     })
50436                     .attr('title', function(d) { return _t('icons.' + d.id); })
50437                     .on('click', function(d) {
50438                         _tagView = d.id;
50439                         corePreferences('raw-tag-editor-view', d.id);
50440
50441                         wrap.selectAll('.raw-tag-option')
50442                             .classed('selected', function(datum) { return datum === d; });
50443
50444                         wrap.selectAll('.tag-text')
50445                             .classed('hide', (d.id !== 'text'))
50446                             .each(setTextareaHeight);
50447
50448                         wrap.selectAll('.tag-list, .add-row')
50449                             .classed('hide', (d.id !== 'list'));
50450                     })
50451                     .each(function(d) {
50452                         select(this)
50453                             .call(svgIcon(d.icon));
50454                     });
50455
50456
50457                 // View as Text
50458                 var textData = rowsToText(rowData);
50459                 var textarea = wrap.selectAll('.tag-text')
50460                     .data([0]);
50461
50462                 textarea = textarea.enter()
50463                     .append('textarea')
50464                     .attr('class', 'tag-text' + (_tagView !== 'text' ? ' hide' : ''))
50465                     .call(utilNoAuto)
50466                     .attr('placeholder', _t('inspector.key_value'))
50467                     .attr('spellcheck', 'false')
50468                     .merge(textarea);
50469
50470                 textarea
50471                     .call(utilGetSetValue, textData)
50472                     .each(setTextareaHeight)
50473                     .on('input', setTextareaHeight)
50474                     .on('blur', textChanged)
50475                     .on('change', textChanged);
50476
50477
50478                 // View as List
50479                 var list = wrap.selectAll('.tag-list')
50480                     .data([0]);
50481
50482                 list = list.enter()
50483                     .append('ul')
50484                     .attr('class', 'tag-list' + (_tagView !== 'list' ? ' hide' : ''))
50485                     .merge(list);
50486
50487
50488                 // Container for the Add button
50489                 var addRowEnter = wrap.selectAll('.add-row')
50490                     .data([0])
50491                     .enter()
50492                     .append('div')
50493                     .attr('class', 'add-row' + (_tagView !== 'list' ? ' hide' : ''));
50494
50495                 addRowEnter
50496                     .append('button')
50497                     .attr('class', 'add-tag')
50498                     .call(svgIcon('#iD-icon-plus', 'light'))
50499                     .on('click', addTag);
50500
50501                 addRowEnter
50502                     .append('div')
50503                     .attr('class', 'space-value');   // preserve space
50504
50505                 addRowEnter
50506                     .append('div')
50507                     .attr('class', 'space-buttons');  // preserve space
50508
50509
50510                 // Tag list items
50511                 var items = list.selectAll('.tag-row')
50512                     .data(rowData, function(d) { return d.key; });
50513
50514                 items.exit()
50515                     .each(unbind)
50516                     .remove();
50517
50518
50519                 // Enter
50520                 var itemsEnter = items.enter()
50521                     .append('li')
50522                     .attr('class', 'tag-row')
50523                     .classed('readonly', isReadOnly);
50524
50525                 var innerWrap = itemsEnter.append('div')
50526                     .attr('class', 'inner-wrap');
50527
50528                 innerWrap
50529                     .append('div')
50530                     .attr('class', 'key-wrap')
50531                     .append('input')
50532                     .property('type', 'text')
50533                     .attr('class', 'key')
50534                     .call(utilNoAuto)
50535                     .on('blur', keyChange)
50536                     .on('change', keyChange);
50537
50538                 innerWrap
50539                     .append('div')
50540                     .attr('class', 'value-wrap')
50541                     .append('input')
50542                     .property('type', 'text')
50543                     .attr('class', 'value')
50544                     .call(utilNoAuto)
50545                     .on('blur', valueChange)
50546                     .on('change', valueChange)
50547                     .on('keydown.push-more', pushMore);
50548
50549                 innerWrap
50550                     .append('button')
50551                     .attr('tabindex', -1)
50552                     .attr('class', 'form-field-button remove')
50553                     .attr('title', _t('icons.remove'))
50554                     .call(svgIcon('#iD-operation-delete'));
50555
50556
50557                 // Update
50558                 items = items
50559                     .merge(itemsEnter)
50560                     .sort(function(a, b) { return a.index - b.index; });
50561
50562                 items
50563                     .each(function(d) {
50564                         var row = select(this);
50565                         var key = row.select('input.key');      // propagate bound data
50566                         var value = row.select('input.value');  // propagate bound data
50567
50568                         if (_entityIDs && taginfo && _state !== 'hover') {
50569                             bindTypeahead(key, value);
50570                         }
50571
50572                         var referenceOptions = { key: d.key };
50573                         if (typeof d.value === 'string') {
50574                             referenceOptions.value = d.value;
50575                         }
50576                         var reference = uiTagReference(referenceOptions);
50577
50578                         if (_state === 'hover') {
50579                             reference.showing(false);
50580                         }
50581
50582                         row.select('.inner-wrap')      // propagate bound data
50583                             .call(reference.button);
50584
50585                         row.call(reference.body);
50586
50587                         row.select('button.remove');   // propagate bound data
50588                     });
50589
50590                 items.selectAll('input.key')
50591                     .attr('title', function(d) { return d.key; })
50592                     .call(utilGetSetValue, function(d) { return d.key; })
50593                     .attr('readonly', function(d) {
50594                         return (isReadOnly(d) || (typeof d.value !== 'string')) || null;
50595                     });
50596
50597                 items.selectAll('input.value')
50598                     .attr('title', function(d) {
50599                         return Array.isArray(d.value) ? d.value.filter(Boolean).join('\n') : d.value;
50600                     })
50601                     .classed('mixed', function(d) {
50602                         return Array.isArray(d.value);
50603                     })
50604                     .attr('placeholder', function(d) {
50605                         return typeof d.value === 'string' ? null : _t('inspector.multiple_values');
50606                     })
50607                     .call(utilGetSetValue, function(d) {
50608                         return typeof d.value === 'string' ? d.value : '';
50609                     })
50610                     .attr('readonly', function(d) {
50611                         return isReadOnly(d) || null;
50612                     });
50613
50614                 items.selectAll('button.remove')
50615                     .on(('PointerEvent' in window ? 'pointer' : 'mouse') + 'down', removeTag);  // 'click' fires too late - #5878
50616
50617             }
50618
50619             function isReadOnly(d) {
50620                 for (var i = 0; i < _readOnlyTags.length; i++) {
50621                     if (d.key.match(_readOnlyTags[i]) !== null) {
50622                         return true;
50623                     }
50624                 }
50625                 return false;
50626             }
50627
50628             function setTextareaHeight() {
50629                 if (_tagView !== 'text') { return; }
50630
50631                 var selection = select(this);
50632                 selection.style('height', null);
50633                 selection.style('height', selection.node().scrollHeight + 5 + 'px');
50634             }
50635
50636             function stringify(s) {
50637                 return JSON.stringify(s).slice(1, -1);   // without leading/trailing "
50638             }
50639
50640             function unstringify(s) {
50641                 var leading = '';
50642                 var trailing = '';
50643                 if (s.length < 1 || s.charAt(0) !== '"') {
50644                     leading = '"';
50645                 }
50646                 if (s.length < 2 || s.charAt(s.length - 1) !== '"' ||
50647                     (s.charAt(s.length - 1) === '"' && s.charAt(s.length - 2) === '\\')
50648                 ) {
50649                     trailing = '"';
50650                 }
50651                 return JSON.parse(leading + s + trailing);
50652             }
50653
50654             function rowsToText(rows) {
50655                 var str = rows
50656                     .filter(function(row) { return row.key && row.key.trim() !== ''; })
50657                     .map(function(row) {
50658                         var rawVal = row.value;
50659                         if (typeof rawVal !== 'string') { rawVal = '*'; }
50660                         var val = rawVal ? stringify(rawVal) : '';
50661                         return stringify(row.key) + '=' + val;
50662                     })
50663                     .join('\n');
50664
50665                 if (_state !== 'hover' && str.length) {
50666                     return str + '\n';
50667                 }
50668                 return  str;
50669             }
50670
50671             function textChanged() {
50672                 var newText = this.value.trim();
50673                 var newTags = {};
50674                 newText.split('\n').forEach(function(row) {
50675                     var m = row.match(/^\s*([^=]+)=(.*)$/);
50676                     if (m !== null) {
50677                         var k = context.cleanTagKey(unstringify(m[1].trim()));
50678                         var v = context.cleanTagValue(unstringify(m[2].trim()));
50679                         newTags[k] = v;
50680                     }
50681                 });
50682
50683                 var tagDiff = utilTagDiff(_tags, newTags);
50684                 if (!tagDiff.length) { return; }
50685
50686                 _pendingChange  = _pendingChange || {};
50687
50688                 tagDiff.forEach(function(change) {
50689                     if (isReadOnly({ key: change.key })) { return; }
50690
50691                     // skip unchanged multiselection placeholders
50692                     if (change.newVal === '*' && typeof change.oldVal !== 'string') { return; }
50693
50694                     if (change.type === '-') {
50695                         _pendingChange[change.key] = undefined;
50696                     } else if (change.type === '+') {
50697                         _pendingChange[change.key] = change.newVal || '';
50698                     }
50699                 });
50700
50701                 if (Object.keys(_pendingChange).length === 0) {
50702                     _pendingChange = null;
50703                     return;
50704                 }
50705
50706                 scheduleChange();
50707             }
50708
50709             function pushMore() {
50710                 // if pressing Tab on the last value field with content, add a blank row
50711                 if (event.keyCode === 9 && !event.shiftKey &&
50712                     section.selection().selectAll('.tag-list li:last-child input.value').node() === this &&
50713                     utilGetSetValue(select(this))) {
50714                     addTag();
50715                 }
50716             }
50717
50718             function bindTypeahead(key, value) {
50719                 if (isReadOnly(key.datum())) { return; }
50720
50721                 if (Array.isArray(value.datum().value)) {
50722                     value.call(uiCombobox(context, 'tag-value')
50723                         .minItems(1)
50724                         .fetcher(function(value, callback) {
50725                             var keyString = utilGetSetValue(key);
50726                             if (!_tags[keyString]) { return; }
50727                             var data = _tags[keyString].filter(Boolean).map(function(tagValue) {
50728                                 return {
50729                                     value: tagValue,
50730                                     title: tagValue
50731                                 };
50732                             });
50733                             callback(data);
50734                         }));
50735                     return;
50736                 }
50737
50738                 var geometry = context.graph().geometry(_entityIDs[0]);
50739
50740                 key.call(uiCombobox(context, 'tag-key')
50741                     .fetcher(function(value, callback) {
50742                         taginfo.keys({
50743                             debounce: true,
50744                             geometry: geometry,
50745                             query: value
50746                         }, function(err, data) {
50747                             if (!err) {
50748                                 var filtered = data.filter(function(d) { return _tags[d.value] === undefined; });
50749                                 callback(sort(value, filtered));
50750                             }
50751                         });
50752                     }));
50753
50754                 value.call(uiCombobox(context, 'tag-value')
50755                     .fetcher(function(value, callback) {
50756                         taginfo.values({
50757                             debounce: true,
50758                             key: utilGetSetValue(key),
50759                             geometry: geometry,
50760                             query: value
50761                         }, function(err, data) {
50762                             if (!err) { callback(sort(value, data)); }
50763                         });
50764                     }));
50765
50766
50767                 function sort(value, data) {
50768                     var sameletter = [];
50769                     var other = [];
50770                     for (var i = 0; i < data.length; i++) {
50771                         if (data[i].value.substring(0, value.length) === value) {
50772                             sameletter.push(data[i]);
50773                         } else {
50774                             other.push(data[i]);
50775                         }
50776                     }
50777                     return sameletter.concat(other);
50778                 }
50779             }
50780
50781             function unbind() {
50782                 var row = select(this);
50783
50784                 row.selectAll('input.key')
50785                     .call(uiCombobox.off, context);
50786
50787                 row.selectAll('input.value')
50788                     .call(uiCombobox.off, context);
50789             }
50790
50791             function keyChange(d) {
50792                 if (select(this).attr('readonly')) { return; }
50793
50794                 var kOld = d.key;
50795
50796                 // exit if we are currently about to delete this row anyway - #6366
50797                 if (_pendingChange && _pendingChange.hasOwnProperty(kOld) && _pendingChange[kOld] === undefined) { return; }
50798
50799                 var kNew = context.cleanTagKey(this.value.trim());
50800
50801                 // allow no change if the key should be readonly
50802                 if (isReadOnly({ key: kNew })) {
50803                     this.value = kOld;
50804                     return;
50805                 }
50806
50807                 if (kNew &&
50808                     kNew !== kOld &&
50809                     _tags[kNew] !== undefined) {
50810                     // new key is already in use, switch focus to the existing row
50811
50812                     this.value = kOld;                // reset the key
50813                     section.selection().selectAll('.tag-list input.value')
50814                         .each(function(d) {
50815                             if (d.key === kNew) {     // send focus to that other value combo instead
50816                                 var input = select(this).node();
50817                                 input.focus();
50818                                 input.select();
50819                             }
50820                         });
50821                     return;
50822                 }
50823
50824
50825                 var row = this.parentNode.parentNode;
50826                 var inputVal = select(row).selectAll('input.value');
50827                 var vNew = context.cleanTagValue(utilGetSetValue(inputVal));
50828
50829                 _pendingChange = _pendingChange || {};
50830
50831                 if (kOld) {
50832                     _pendingChange[kOld] = undefined;
50833                 }
50834
50835                 _pendingChange[kNew] = vNew;
50836
50837                 // update the ordered key index so this row doesn't change position
50838                 var existingKeyIndex = _orderedKeys.indexOf(kOld);
50839                 if (existingKeyIndex !== -1) { _orderedKeys[existingKeyIndex] = kNew; }
50840
50841                 d.key = kNew;    // update datum to avoid exit/enter on tag update
50842                 d.value = vNew;
50843
50844                 this.value = kNew;
50845                 utilGetSetValue(inputVal, vNew);
50846                 scheduleChange();
50847             }
50848
50849             function valueChange(d) {
50850                 if (isReadOnly(d)) { return; }
50851
50852                 // exit if this is a multiselection and no value was entered
50853                 if (typeof d.value !== 'string' && !this.value) { return; }
50854
50855                 // exit if we are currently about to delete this row anyway - #6366
50856                 if (_pendingChange && _pendingChange.hasOwnProperty(d.key) && _pendingChange[d.key] === undefined) { return; }
50857
50858                 _pendingChange = _pendingChange || {};
50859
50860                 _pendingChange[d.key] = context.cleanTagValue(this.value);
50861                 scheduleChange();
50862             }
50863
50864             function removeTag(d) {
50865                 if (isReadOnly(d)) { return; }
50866
50867                 if (d.key === '') {    // removing the blank row
50868                     _showBlank = false;
50869                     section.reRender();
50870
50871                 } else {
50872                     // remove the key from the ordered key index
50873                     _orderedKeys = _orderedKeys.filter(function(key) { return key !== d.key; });
50874
50875                     _pendingChange  = _pendingChange || {};
50876                     _pendingChange[d.key] = undefined;
50877                     scheduleChange();
50878                 }
50879             }
50880
50881             function addTag() {
50882                 // Delay render in case this click is blurring an edited combo.
50883                 // Without the setTimeout, the `content` render would wipe out the pending tag change.
50884                 window.setTimeout(function() {
50885                     _showBlank = true;
50886                     section.reRender();
50887                     section.selection().selectAll('.tag-list li:last-child input.key').node().focus();
50888                 }, 20);
50889             }
50890
50891             function scheduleChange() {
50892                 // Cache IDs in case the editor is reloaded before the change event is called. - #6028
50893                 var entityIDs = _entityIDs;
50894
50895                 // Delay change in case this change is blurring an edited combo. - #5878
50896                 window.setTimeout(function() {
50897                     if (!_pendingChange) { return; }
50898
50899                     dispatch$1.call('change', this, entityIDs, _pendingChange);
50900                     _pendingChange = null;
50901                 }, 10);
50902             }
50903
50904
50905             section.state = function(val) {
50906                 if (!arguments.length) { return _state; }
50907                 if (_state !== val) {
50908                     _orderedKeys = [];
50909                     _state = val;
50910                 }
50911                 return section;
50912             };
50913
50914
50915             section.presets = function(val) {
50916                 if (!arguments.length) { return _presets; }
50917                 _presets = val;
50918                 if (_presets && _presets.length && _presets[0].isFallback()) {
50919                     section.disclosureExpanded(true);
50920                 } else {
50921                     section.disclosureExpanded(null);
50922                 }
50923                 return section;
50924             };
50925
50926
50927             section.tags = function(val) {
50928                 if (!arguments.length) { return _tags; }
50929                 _tags = val;
50930                 return section;
50931             };
50932
50933
50934             section.entityIDs = function(val) {
50935                 if (!arguments.length) { return _entityIDs; }
50936                 if (!_entityIDs || !val || !utilArrayIdentical(_entityIDs, val)) {
50937                     _entityIDs = val;
50938                     _orderedKeys = [];
50939                 }
50940                 return section;
50941             };
50942
50943
50944             // pass an array of regular expressions to test against the tag key
50945             section.readOnlyTags = function(val) {
50946                 if (!arguments.length) { return _readOnlyTags; }
50947                 _readOnlyTags = val;
50948                 return section;
50949             };
50950
50951
50952             return utilRebind(section, dispatch$1, 'on');
50953         }
50954
50955         function uiDataEditor(context) {
50956             var dataHeader = uiDataHeader();
50957             var rawTagEditor = uiSectionRawTagEditor('custom-data-tag-editor', context)
50958                 .expandedByDefault(true)
50959                 .readOnlyTags([/./]);
50960             var _datum;
50961
50962
50963             function dataEditor(selection) {
50964
50965                 var header = selection.selectAll('.header')
50966                     .data([0]);
50967
50968                 var headerEnter = header.enter()
50969                     .append('div')
50970                     .attr('class', 'header fillL');
50971
50972                 headerEnter
50973                     .append('button')
50974                     .attr('class', 'close')
50975                     .on('click', function() {
50976                         context.enter(modeBrowse(context));
50977                     })
50978                     .call(svgIcon('#iD-icon-close'));
50979
50980                 headerEnter
50981                     .append('h3')
50982                     .text(_t('map_data.title'));
50983
50984
50985                 var body = selection.selectAll('.body')
50986                     .data([0]);
50987
50988                 body = body.enter()
50989                     .append('div')
50990                     .attr('class', 'body')
50991                     .merge(body);
50992
50993                 var editor = body.selectAll('.data-editor')
50994                     .data([0]);
50995
50996                 // enter/update
50997                 editor.enter()
50998                     .append('div')
50999                     .attr('class', 'modal-section data-editor')
51000                     .merge(editor)
51001                     .call(dataHeader.datum(_datum));
51002
51003                 var rte = body.selectAll('.raw-tag-editor')
51004                     .data([0]);
51005
51006                 // enter/update
51007                 rte.enter()
51008                     .append('div')
51009                     .attr('class', 'raw-tag-editor data-editor')
51010                     .merge(rte)
51011                     .call(rawTagEditor
51012                         .tags((_datum && _datum.properties) || {})
51013                         .state('hover')
51014                         .render
51015                     )
51016                     .selectAll('textarea.tag-text')
51017                     .attr('readonly', true)
51018                     .classed('readonly', true);
51019             }
51020
51021
51022             dataEditor.datum = function(val) {
51023                 if (!arguments.length) { return _datum; }
51024                 _datum = val;
51025                 return this;
51026             };
51027
51028
51029             return dataEditor;
51030         }
51031
51032         function modeSelectData(context, selectedDatum) {
51033             var mode = {
51034                 id: 'select-data',
51035                 button: 'browse'
51036             };
51037
51038             var keybinding = utilKeybinding('select-data');
51039             var dataEditor = uiDataEditor(context);
51040
51041             var behaviors = [
51042                 behaviorBreathe(),
51043                 behaviorHover(context),
51044                 behaviorSelect(context),
51045                 behaviorLasso(context),
51046                 modeDragNode(context).behavior,
51047                 modeDragNote(context).behavior
51048             ];
51049
51050
51051             // class the data as selected, or return to browse mode if the data is gone
51052             function selectData(drawn) {
51053                 var selection = context.surface().selectAll('.layer-mapdata .data' + selectedDatum.__featurehash__);
51054
51055                 if (selection.empty()) {
51056                     // Return to browse mode if selected DOM elements have
51057                     // disappeared because the user moved them out of view..
51058                     var source = event && event.type === 'zoom' && event.sourceEvent;
51059                     if (drawn && source && (source.type === 'pointermove' || source.type === 'mousemove' || source.type === 'touchmove')) {
51060                         context.enter(modeBrowse(context));
51061                     }
51062                 } else {
51063                     selection.classed('selected', true);
51064                 }
51065             }
51066
51067
51068             function esc() {
51069                 if (context.container().select('.combobox').size()) { return; }
51070                 context.enter(modeBrowse(context));
51071             }
51072
51073
51074             mode.zoomToSelected = function() {
51075                 var extent = geoExtent(d3_geoBounds(selectedDatum));
51076                 context.map().centerZoomEase(extent.center(), context.map().trimmedExtentZoom(extent));
51077             };
51078
51079
51080             mode.enter = function() {
51081                 behaviors.forEach(context.install);
51082
51083                 keybinding
51084                     .on(_t('inspector.zoom_to.key'), mode.zoomToSelected)
51085                     .on('⎋', esc, true);
51086
51087                 select(document)
51088                     .call(keybinding);
51089
51090                 selectData();
51091
51092                 var sidebar = context.ui().sidebar;
51093                 sidebar.show(dataEditor.datum(selectedDatum));
51094
51095                 // expand the sidebar, avoid obscuring the data if needed
51096                 var extent = geoExtent(d3_geoBounds(selectedDatum));
51097                 sidebar.expand(sidebar.intersects(extent));
51098
51099                 context.map()
51100                     .on('drawn.select-data', selectData);
51101             };
51102
51103
51104             mode.exit = function() {
51105                 behaviors.forEach(context.uninstall);
51106
51107                 select(document)
51108                     .call(keybinding.unbind);
51109
51110                 context.surface()
51111                     .selectAll('.layer-mapdata .selected')
51112                     .classed('selected hover', false);
51113
51114                 context.map()
51115                     .on('drawn.select-data', null);
51116
51117                 context.ui().sidebar
51118                     .hide();
51119             };
51120
51121
51122             return mode;
51123         }
51124
51125         function uiImproveOsmComments() {
51126           var _qaItem;
51127
51128           function issueComments(selection) {
51129             // make the div immediately so it appears above the buttons
51130             var comments = selection.selectAll('.comments-container')
51131               .data([0]);
51132
51133             comments = comments.enter()
51134               .append('div')
51135                 .attr('class', 'comments-container')
51136               .merge(comments);
51137
51138             // must retrieve comments from API before they can be displayed
51139             services.improveOSM.getComments(_qaItem)
51140               .then(function (d) {
51141                 if (!d.comments) { return; } // nothing to do here
51142
51143                 var commentEnter = comments.selectAll('.comment')
51144                   .data(d.comments)
51145                   .enter()
51146                   .append('div')
51147                     .attr('class', 'comment');
51148
51149                 commentEnter
51150                   .append('div')
51151                     .attr('class', 'comment-avatar')
51152                     .call(svgIcon('#iD-icon-avatar', 'comment-avatar-icon'));
51153
51154                 var mainEnter = commentEnter
51155                   .append('div')
51156                   .attr('class', 'comment-main');
51157
51158                 var metadataEnter = mainEnter
51159                   .append('div')
51160                     .attr('class', 'comment-metadata');
51161
51162                 metadataEnter
51163                   .append('div')
51164                     .attr('class', 'comment-author')
51165                     .each(function(d) {
51166                       var osm = services.osm;
51167                       var selection = select(this);
51168                       if (osm && d.username) {
51169                         selection = selection
51170                           .append('a')
51171                           .attr('class', 'comment-author-link')
51172                           .attr('href', osm.userURL(d.username))
51173                           .attr('tabindex', -1)
51174                           .attr('target', '_blank');
51175                       }
51176                       selection
51177                         .text(function (d) { return d.username; });
51178                     });
51179
51180                 metadataEnter
51181                   .append('div')
51182                     .attr('class', 'comment-date')
51183                     .text(function (d) { return _t('note.status.commented', { when: localeDateString(d.timestamp) }); });
51184
51185                 mainEnter
51186                   .append('div')
51187                     .attr('class', 'comment-text')
51188                   .append('p')
51189                     .text(function (d) { return d.text; });
51190             })
51191             .catch(function (err) {
51192               console.log(err); // eslint-disable-line no-console
51193             });
51194           }
51195
51196           function localeDateString(s) {
51197             if (!s) { return null; }
51198             var options = { day: 'numeric', month: 'short', year: 'numeric' };
51199             var d = new Date(s * 1000); // timestamp is served in seconds, date takes ms
51200             if (isNaN(d.getTime())) { return null; }
51201             return d.toLocaleDateString(_mainLocalizer.localeCode(), options);
51202           }
51203
51204           issueComments.issue = function(val) {
51205             if (!arguments.length) { return _qaItem; }
51206             _qaItem = val;
51207             return issueComments;
51208           };
51209
51210           return issueComments;
51211         }
51212
51213         function uiImproveOsmDetails(context) {
51214           var _qaItem;
51215
51216
51217           function issueDetail(d) {
51218             if (d.desc) { return d.desc; }
51219             var issueKey = d.issueKey;
51220             d.replacements = d.replacements || {};
51221             d.replacements.default = _t('inspector.unknown');  // special key `default` works as a fallback string
51222             return _t(("QA.improveOSM.error_types." + issueKey + ".description"), d.replacements);
51223           }
51224
51225
51226           function improveOsmDetails(selection) {
51227             var details = selection.selectAll('.error-details')
51228               .data(
51229                 (_qaItem ? [_qaItem] : []),
51230                 function (d) { return ((d.id) + "-" + (d.status || 0)); }
51231               );
51232
51233             details.exit()
51234               .remove();
51235
51236             var detailsEnter = details.enter()
51237               .append('div')
51238                 .attr('class', 'error-details qa-details-container');
51239
51240
51241             // description
51242             var descriptionEnter = detailsEnter
51243               .append('div')
51244                 .attr('class', 'qa-details-subsection');
51245
51246             descriptionEnter
51247               .append('h4')
51248                 .text(function () { return _t('QA.keepRight.detail_description'); });
51249
51250             descriptionEnter
51251               .append('div')
51252                 .attr('class', 'qa-details-description-text')
51253                 .html(issueDetail);
51254
51255             // If there are entity links in the error message..
51256             var relatedEntities = [];
51257             descriptionEnter.selectAll('.error_entity_link, .error_object_link')
51258               .each(function() {
51259                 var link = select(this);
51260                 var isObjectLink = link.classed('error_object_link');
51261                 var entityID = isObjectLink ?
51262                   (utilEntityRoot(_qaItem.objectType) + _qaItem.objectId)
51263                   : this.textContent;
51264                 var entity = context.hasEntity(entityID);
51265
51266                 relatedEntities.push(entityID);
51267
51268                 // Add click handler
51269                 link
51270                   .on('mouseenter', function () {
51271                     utilHighlightEntities([entityID], true, context);
51272                   })
51273                   .on('mouseleave', function () {
51274                     utilHighlightEntities([entityID], false, context);
51275                   })
51276                   .on('click', function () {
51277                     event.preventDefault();
51278
51279                     utilHighlightEntities([entityID], false, context);
51280
51281                     var osmlayer = context.layers().layer('osm');
51282                     if (!osmlayer.enabled()) {
51283                       osmlayer.enabled(true);
51284                     }
51285
51286                     context.map().centerZoom(_qaItem.loc, 20);
51287
51288                     if (entity) {
51289                       context.enter(modeSelect(context, [entityID]));
51290                     } else {
51291                       context.loadEntity(entityID, function () {
51292                         context.enter(modeSelect(context, [entityID]));
51293                       });
51294                     }
51295                   });
51296
51297                 // Replace with friendly name if possible
51298                 // (The entity may not yet be loaded into the graph)
51299                 if (entity) {
51300                   var name = utilDisplayName(entity);  // try to use common name
51301
51302                   if (!name && !isObjectLink) {
51303                     var preset = _mainPresetIndex.match(entity, context.graph());
51304                     name = preset && !preset.isFallback() && preset.name();  // fallback to preset name
51305                   }
51306
51307                   if (name) {
51308                     this.innerText = name;
51309                   }
51310                 }
51311               });
51312
51313             // Don't hide entities related to this error - #5880
51314             context.features().forceVisible(relatedEntities);
51315             context.map().pan([0,0]);  // trigger a redraw
51316           }
51317
51318           improveOsmDetails.issue = function(val) {
51319             if (!arguments.length) { return _qaItem; }
51320             _qaItem = val;
51321             return improveOsmDetails;
51322           };
51323
51324           return improveOsmDetails;
51325         }
51326
51327         function uiImproveOsmHeader() {
51328           var _qaItem;
51329
51330
51331           function issueTitle(d) {
51332             var issueKey = d.issueKey;
51333             d.replacements = d.replacements || {};
51334             d.replacements.default = _t('inspector.unknown');  // special key `default` works as a fallback string
51335             return _t(("QA.improveOSM.error_types." + issueKey + ".title"), d.replacements);
51336           }
51337
51338
51339           function improveOsmHeader(selection) {
51340             var header = selection.selectAll('.qa-header')
51341               .data(
51342                 (_qaItem ? [_qaItem] : []),
51343                 function (d) { return ((d.id) + "-" + (d.status || 0)); }
51344               );
51345
51346             header.exit()
51347               .remove();
51348
51349             var headerEnter = header.enter()
51350               .append('div')
51351                 .attr('class', 'qa-header');
51352
51353             var svgEnter = headerEnter
51354               .append('div')
51355                 .attr('class', 'qa-header-icon')
51356                 .classed('new', function (d) { return d.id < 0; })
51357               .append('svg')
51358                 .attr('width', '20px')
51359                 .attr('height', '30px')
51360                 .attr('viewbox', '0 0 20 30')
51361                 .attr('class', function (d) { return ("preset-icon-28 qaItem " + (d.service) + " itemId-" + (d.id) + " itemType-" + (d.itemType)); });
51362
51363             svgEnter
51364               .append('polygon')
51365                 .attr('fill', 'currentColor')
51366                 .attr('class', 'qaItem-fill')
51367                 .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');
51368
51369             svgEnter
51370               .append('use')
51371                 .attr('class', 'icon-annotation')
51372                 .attr('width', '13px')
51373                 .attr('height', '13px')
51374                 .attr('transform', 'translate(3.5, 5)')
51375                 .attr('xlink:href', function (d) {
51376                   var picon = d.icon;
51377                   if (!picon) {
51378                     return '';
51379                   } else {
51380                     var isMaki = /^maki-/.test(picon);
51381                     return ("#" + picon + (isMaki ? '-11' : ''));
51382                   }
51383                 });
51384
51385             headerEnter
51386               .append('div')
51387                 .attr('class', 'qa-header-label')
51388                 .text(issueTitle);
51389           }
51390
51391           improveOsmHeader.issue = function(val) {
51392             if (!arguments.length) { return _qaItem; }
51393             _qaItem = val;
51394             return improveOsmHeader;
51395           };
51396
51397           return improveOsmHeader;
51398         }
51399
51400         function uiImproveOsmEditor(context) {
51401           var dispatch$1 = dispatch('change');
51402           var qaDetails = uiImproveOsmDetails(context);
51403           var qaComments = uiImproveOsmComments();
51404           var qaHeader = uiImproveOsmHeader();
51405
51406           var _qaItem;
51407
51408           function improveOsmEditor(selection) {
51409
51410             var headerEnter = selection.selectAll('.header')
51411               .data([0])
51412               .enter()
51413               .append('div')
51414                 .attr('class', 'header fillL');
51415
51416             headerEnter
51417               .append('button')
51418                 .attr('class', 'close')
51419                 .on('click', function () { return context.enter(modeBrowse(context)); })
51420                 .call(svgIcon('#iD-icon-close'));
51421
51422             headerEnter
51423               .append('h3')
51424                 .text(_t('QA.improveOSM.title'));
51425
51426             var body = selection.selectAll('.body')
51427               .data([0]);
51428
51429             body = body.enter()
51430               .append('div')
51431                 .attr('class', 'body')
51432               .merge(body);
51433
51434             var editor = body.selectAll('.qa-editor')
51435               .data([0]);
51436
51437             editor.enter()
51438               .append('div')
51439                 .attr('class', 'modal-section qa-editor')
51440               .merge(editor)
51441                 .call(qaHeader.issue(_qaItem))
51442                 .call(qaDetails.issue(_qaItem))
51443                 .call(qaComments.issue(_qaItem))
51444                 .call(improveOsmSaveSection);
51445           }
51446
51447           function improveOsmSaveSection(selection) {
51448             var isSelected = (_qaItem && _qaItem.id === context.selectedErrorID());
51449             var isShown = (_qaItem && (isSelected || _qaItem.newComment || _qaItem.comment));
51450             var saveSection = selection.selectAll('.qa-save')
51451               .data(
51452                 (isShown ? [_qaItem] : []),
51453                 function (d) { return ((d.id) + "-" + (d.status || 0)); }
51454               );
51455
51456             // exit
51457             saveSection.exit()
51458               .remove();
51459
51460             // enter
51461             var saveSectionEnter = saveSection.enter()
51462               .append('div')
51463                 .attr('class', 'qa-save save-section cf');
51464
51465             saveSectionEnter
51466               .append('h4')
51467                 .attr('class', '.qa-save-header')
51468                 .text(_t('note.newComment'));
51469
51470             saveSectionEnter
51471               .append('textarea')
51472                 .attr('class', 'new-comment-input')
51473                 .attr('placeholder', _t('QA.keepRight.comment_placeholder'))
51474                 .attr('maxlength', 1000)
51475                 .property('value', function (d) { return d.newComment; })
51476                 .call(utilNoAuto)
51477                 .on('input', changeInput)
51478                 .on('blur', changeInput);
51479
51480             // update
51481             saveSection = saveSectionEnter
51482               .merge(saveSection)
51483                 .call(qaSaveButtons);
51484
51485             function changeInput() {
51486               var input = select(this);
51487               var val = input.property('value').trim();
51488
51489               if (val === '') {
51490                 val = undefined;
51491               }
51492
51493               // store the unsaved comment with the issue itself
51494               _qaItem = _qaItem.update({ newComment: val });
51495
51496               var qaService = services.improveOSM;
51497               if (qaService) {
51498                 qaService.replaceItem(_qaItem);
51499               }
51500
51501               saveSection
51502                 .call(qaSaveButtons);
51503             }
51504           }
51505
51506           function qaSaveButtons(selection) {
51507             var isSelected = (_qaItem && _qaItem.id === context.selectedErrorID());
51508             var buttonSection = selection.selectAll('.buttons')
51509               .data((isSelected ? [_qaItem] : []), function (d) { return d.status + d.id; });
51510
51511             // exit
51512             buttonSection.exit()
51513               .remove();
51514
51515             // enter
51516             var buttonEnter = buttonSection.enter()
51517               .append('div')
51518                 .attr('class', 'buttons');
51519
51520             buttonEnter
51521               .append('button')
51522                 .attr('class', 'button comment-button action')
51523                 .text(_t('QA.keepRight.save_comment'));
51524
51525             buttonEnter
51526               .append('button')
51527                 .attr('class', 'button close-button action');
51528
51529             buttonEnter
51530               .append('button')
51531                 .attr('class', 'button ignore-button action');
51532
51533             // update
51534             buttonSection = buttonSection
51535               .merge(buttonEnter);
51536
51537             buttonSection.select('.comment-button')
51538               .attr('disabled', function (d) { return d.newComment ? null : true; })
51539               .on('click.comment', function(d) {
51540                 this.blur();    // avoid keeping focus on the button - #4641
51541                 var qaService = services.improveOSM;
51542                 if (qaService) {
51543                   qaService.postUpdate(d, function (err, item) { return dispatch$1.call('change', item); });
51544                 }
51545               });
51546
51547             buttonSection.select('.close-button')
51548               .text(function (d) {
51549                 var andComment = (d.newComment ? '_comment' : '');
51550                 return _t(("QA.keepRight.close" + andComment));
51551               })
51552               .on('click.close', function(d) {
51553                 this.blur();    // avoid keeping focus on the button - #4641
51554                 var qaService = services.improveOSM;
51555                 if (qaService) {
51556                   d.newStatus = 'SOLVED';
51557                   qaService.postUpdate(d, function (err, item) { return dispatch$1.call('change', item); });
51558                 }
51559               });
51560
51561             buttonSection.select('.ignore-button')
51562               .text(function (d) {
51563                 var andComment = (d.newComment ? '_comment' : '');
51564                 return _t(("QA.keepRight.ignore" + andComment));
51565               })
51566               .on('click.ignore', function(d) {
51567                 this.blur();    // avoid keeping focus on the button - #4641
51568                 var qaService = services.improveOSM;
51569                 if (qaService) {
51570                   d.newStatus = 'INVALID';
51571                   qaService.postUpdate(d, function (err, item) { return dispatch$1.call('change', item); });
51572                 }
51573               });
51574           }
51575
51576           // NOTE: Don't change method name until UI v3 is merged
51577           improveOsmEditor.error = function(val) {
51578             if (!arguments.length) { return _qaItem; }
51579             _qaItem = val;
51580             return improveOsmEditor;
51581           };
51582
51583           return utilRebind(improveOsmEditor, dispatch$1, 'on');
51584         }
51585
51586         function uiKeepRightDetails(context) {
51587           var _qaItem;
51588
51589
51590           function issueDetail(d) {
51591             var itemType = d.itemType;
51592             var parentIssueType = d.parentIssueType;
51593             var unknown = _t('inspector.unknown');
51594             var replacements = d.replacements || {};
51595             replacements.default = unknown;  // special key `default` works as a fallback string
51596
51597             var detail = _t(("QA.keepRight.errorTypes." + itemType + ".description"), replacements);
51598             if (detail === unknown) {
51599               detail = _t(("QA.keepRight.errorTypes." + parentIssueType + ".description"), replacements);
51600             }
51601             return detail;
51602           }
51603
51604
51605           function keepRightDetails(selection) {
51606             var details = selection.selectAll('.error-details')
51607               .data(
51608                 (_qaItem ? [_qaItem] : []),
51609                 function (d) { return ((d.id) + "-" + (d.status || 0)); }
51610               );
51611
51612             details.exit()
51613               .remove();
51614
51615             var detailsEnter = details.enter()
51616               .append('div')
51617                 .attr('class', 'error-details qa-details-container');
51618
51619             // description
51620             var descriptionEnter = detailsEnter
51621               .append('div')
51622                 .attr('class', 'qa-details-subsection');
51623
51624             descriptionEnter
51625               .append('h4')
51626                 .text(function () { return _t('QA.keepRight.detail_description'); });
51627
51628             descriptionEnter
51629               .append('div')
51630                 .attr('class', 'qa-details-description-text')
51631                 .html(issueDetail);
51632
51633             // If there are entity links in the error message..
51634             var relatedEntities = [];
51635             descriptionEnter.selectAll('.error_entity_link, .error_object_link')
51636               .each(function() {
51637                 var link = select(this);
51638                 var isObjectLink = link.classed('error_object_link');
51639                 var entityID = isObjectLink ?
51640                   (utilEntityRoot(_qaItem.objectType) + _qaItem.objectId)
51641                   : this.textContent;
51642                 var entity = context.hasEntity(entityID);
51643
51644                 relatedEntities.push(entityID);
51645
51646                 // Add click handler
51647                 link
51648                   .on('mouseenter', function () {
51649                     utilHighlightEntities([entityID], true, context);
51650                   })
51651                   .on('mouseleave', function () {
51652                     utilHighlightEntities([entityID], false, context);
51653                   })
51654                   .on('click', function () {
51655                     event.preventDefault();
51656
51657                     utilHighlightEntities([entityID], false, context);
51658
51659                     var osmlayer = context.layers().layer('osm');
51660                     if (!osmlayer.enabled()) {
51661                       osmlayer.enabled(true);
51662                     }
51663
51664                     context.map().centerZoomEase(_qaItem.loc, 20);
51665
51666                     if (entity) {
51667                       context.enter(modeSelect(context, [entityID]));
51668                     } else {
51669                       context.loadEntity(entityID, function () {
51670                         context.enter(modeSelect(context, [entityID]));
51671                       });
51672                     }
51673                   });
51674
51675                 // Replace with friendly name if possible
51676                 // (The entity may not yet be loaded into the graph)
51677                 if (entity) {
51678                   var name = utilDisplayName(entity);  // try to use common name
51679
51680                   if (!name && !isObjectLink) {
51681                     var preset = _mainPresetIndex.match(entity, context.graph());
51682                     name = preset && !preset.isFallback() && preset.name();  // fallback to preset name
51683                   }
51684
51685                   if (name) {
51686                     this.innerText = name;
51687                   }
51688                 }
51689               });
51690
51691             // Don't hide entities related to this issue - #5880
51692             context.features().forceVisible(relatedEntities);
51693             context.map().pan([0,0]);  // trigger a redraw
51694           }
51695
51696           keepRightDetails.issue = function(val) {
51697             if (!arguments.length) { return _qaItem; }
51698             _qaItem = val;
51699             return keepRightDetails;
51700           };
51701
51702           return keepRightDetails;
51703         }
51704
51705         function uiKeepRightHeader() {
51706           var _qaItem;
51707
51708
51709           function issueTitle(d) {
51710             var itemType = d.itemType;
51711             var parentIssueType = d.parentIssueType;
51712             var unknown = _t('inspector.unknown');
51713             var replacements = d.replacements || {};
51714             replacements.default = unknown;  // special key `default` works as a fallback string
51715
51716             var title = _t(("QA.keepRight.errorTypes." + itemType + ".title"), replacements);
51717             if (title === unknown) {
51718               title = _t(("QA.keepRight.errorTypes." + parentIssueType + ".title"), replacements);
51719             }
51720             return title;
51721           }
51722
51723
51724           function keepRightHeader(selection) {
51725             var header = selection.selectAll('.qa-header')
51726               .data(
51727                 (_qaItem ? [_qaItem] : []),
51728                 function (d) { return ((d.id) + "-" + (d.status || 0)); }
51729               );
51730
51731             header.exit()
51732               .remove();
51733
51734             var headerEnter = header.enter()
51735               .append('div')
51736                 .attr('class', 'qa-header');
51737
51738             var iconEnter = headerEnter
51739               .append('div')
51740                 .attr('class', 'qa-header-icon')
51741                 .classed('new', function (d) { return d.id < 0; });
51742
51743             iconEnter
51744               .append('div')
51745                 .attr('class', function (d) { return ("preset-icon-28 qaItem " + (d.service) + " itemId-" + (d.id) + " itemType-" + (d.parentIssueType)); })
51746                 .call(svgIcon('#iD-icon-bolt', 'qaItem-fill'));
51747
51748             headerEnter
51749               .append('div')
51750                 .attr('class', 'qa-header-label')
51751                 .text(issueTitle);
51752           }
51753
51754
51755           keepRightHeader.issue = function(val) {
51756             if (!arguments.length) { return _qaItem; }
51757             _qaItem = val;
51758             return keepRightHeader;
51759           };
51760
51761           return keepRightHeader;
51762         }
51763
51764         function uiViewOnKeepRight() {
51765           var _qaItem;
51766
51767           function viewOnKeepRight(selection) {
51768             var url;
51769             if (services.keepRight && (_qaItem instanceof QAItem)) {
51770               url = services.keepRight.issueURL(_qaItem);
51771             }
51772
51773             var link = selection.selectAll('.view-on-keepRight')
51774               .data(url ? [url] : []);
51775
51776             // exit
51777             link.exit()
51778               .remove();
51779
51780             // enter
51781             var linkEnter = link.enter()
51782               .append('a')
51783                 .attr('class', 'view-on-keepRight')
51784                 .attr('target', '_blank')
51785                 .attr('rel', 'noopener') // security measure
51786                 .attr('href', function (d) { return d; })
51787                 .call(svgIcon('#iD-icon-out-link', 'inline'));
51788
51789             linkEnter
51790               .append('span')
51791                 .text(_t('inspector.view_on_keepRight'));
51792           }
51793
51794           viewOnKeepRight.what = function(val) {
51795             if (!arguments.length) { return _qaItem; }
51796             _qaItem = val;
51797             return viewOnKeepRight;
51798           };
51799
51800           return viewOnKeepRight;
51801         }
51802
51803         function uiKeepRightEditor(context) {
51804           var dispatch$1 = dispatch('change');
51805           var qaDetails = uiKeepRightDetails(context);
51806           var qaHeader = uiKeepRightHeader();
51807
51808           var _qaItem;
51809
51810           function keepRightEditor(selection) {
51811
51812             var headerEnter = selection.selectAll('.header')
51813               .data([0])
51814               .enter()
51815               .append('div')
51816                 .attr('class', 'header fillL');
51817
51818             headerEnter
51819               .append('button')
51820                 .attr('class', 'close')
51821                 .on('click', function () { return context.enter(modeBrowse(context)); })
51822                 .call(svgIcon('#iD-icon-close'));
51823
51824             headerEnter
51825               .append('h3')
51826                 .text(_t('QA.keepRight.title'));
51827
51828
51829             var body = selection.selectAll('.body')
51830               .data([0]);
51831
51832             body = body.enter()
51833               .append('div')
51834                 .attr('class', 'body')
51835               .merge(body);
51836
51837             var editor = body.selectAll('.qa-editor')
51838               .data([0]);
51839
51840             editor.enter()
51841               .append('div')
51842                 .attr('class', 'modal-section qa-editor')
51843               .merge(editor)
51844                 .call(qaHeader.issue(_qaItem))
51845                 .call(qaDetails.issue(_qaItem))
51846                 .call(keepRightSaveSection);
51847
51848
51849             var footer = selection.selectAll('.footer')
51850               .data([0]);
51851
51852             footer.enter()
51853               .append('div')
51854               .attr('class', 'footer')
51855               .merge(footer)
51856               .call(uiViewOnKeepRight().what(_qaItem));
51857           }
51858
51859
51860           function keepRightSaveSection(selection) {
51861             var isSelected = (_qaItem && _qaItem.id === context.selectedErrorID());
51862             var isShown = (_qaItem && (isSelected || _qaItem.newComment || _qaItem.comment));
51863             var saveSection = selection.selectAll('.qa-save')
51864               .data(
51865                 (isShown ? [_qaItem] : []),
51866                 function (d) { return ((d.id) + "-" + (d.status || 0)); }
51867               );
51868
51869             // exit
51870             saveSection.exit()
51871               .remove();
51872
51873             // enter
51874             var saveSectionEnter = saveSection.enter()
51875               .append('div')
51876                 .attr('class', 'qa-save save-section cf');
51877
51878             saveSectionEnter
51879               .append('h4')
51880                 .attr('class', '.qa-save-header')
51881                 .text(_t('QA.keepRight.comment'));
51882
51883             saveSectionEnter
51884               .append('textarea')
51885                 .attr('class', 'new-comment-input')
51886                 .attr('placeholder', _t('QA.keepRight.comment_placeholder'))
51887                 .attr('maxlength', 1000)
51888                 .property('value', function (d) { return d.newComment || d.comment; })
51889                 .call(utilNoAuto)
51890                 .on('input', changeInput)
51891                 .on('blur', changeInput);
51892
51893             // update
51894             saveSection = saveSectionEnter
51895               .merge(saveSection)
51896                 .call(qaSaveButtons);
51897
51898             function changeInput() {
51899               var input = select(this);
51900               var val = input.property('value').trim();
51901
51902               if (val === _qaItem.comment) {
51903                 val = undefined;
51904               }
51905
51906               // store the unsaved comment with the issue itself
51907               _qaItem = _qaItem.update({ newComment: val });
51908
51909               var qaService = services.keepRight;
51910               if (qaService) {
51911                 qaService.replaceItem(_qaItem);  // update keepright cache
51912               }
51913
51914               saveSection
51915                 .call(qaSaveButtons);
51916             }
51917           }
51918
51919
51920           function qaSaveButtons(selection) {
51921             var isSelected = (_qaItem && _qaItem.id === context.selectedErrorID());
51922             var buttonSection = selection.selectAll('.buttons')
51923               .data((isSelected ? [_qaItem] : []), function (d) { return d.status + d.id; });
51924
51925             // exit
51926             buttonSection.exit()
51927               .remove();
51928
51929             // enter
51930             var buttonEnter = buttonSection.enter()
51931               .append('div')
51932                 .attr('class', 'buttons');
51933
51934             buttonEnter
51935               .append('button')
51936                 .attr('class', 'button comment-button action')
51937                 .text(_t('QA.keepRight.save_comment'));
51938
51939             buttonEnter
51940               .append('button')
51941                 .attr('class', 'button close-button action');
51942
51943             buttonEnter
51944               .append('button')
51945                 .attr('class', 'button ignore-button action');
51946
51947             // update
51948             buttonSection = buttonSection
51949               .merge(buttonEnter);
51950
51951             buttonSection.select('.comment-button')   // select and propagate data
51952               .attr('disabled', function (d) { return d.newComment ? null : true; })
51953               .on('click.comment', function(d) {
51954                 this.blur();    // avoid keeping focus on the button - #4641
51955                 var qaService = services.keepRight;
51956                 if (qaService) {
51957                   qaService.postUpdate(d, function (err, item) { return dispatch$1.call('change', item); });
51958                 }
51959               });
51960
51961             buttonSection.select('.close-button')   // select and propagate data
51962               .text(function (d) {
51963                 var andComment = (d.newComment ? '_comment' : '');
51964                 return _t(("QA.keepRight.close" + andComment));
51965               })
51966               .on('click.close', function(d) {
51967                 this.blur();    // avoid keeping focus on the button - #4641
51968                 var qaService = services.keepRight;
51969                 if (qaService) {
51970                   d.newStatus = 'ignore_t';   // ignore temporarily (item fixed)
51971                   qaService.postUpdate(d, function (err, item) { return dispatch$1.call('change', item); });
51972                 }
51973               });
51974
51975             buttonSection.select('.ignore-button')   // select and propagate data
51976               .text(function (d) {
51977                 var andComment = (d.newComment ? '_comment' : '');
51978                 return _t(("QA.keepRight.ignore" + andComment));
51979               })
51980               .on('click.ignore', function(d) {
51981                 this.blur();    // avoid keeping focus on the button - #4641
51982                 var qaService = services.keepRight;
51983                 if (qaService) {
51984                   d.newStatus = 'ignore';   // ignore permanently (false positive)
51985                   qaService.postUpdate(d, function (err, item) { return dispatch$1.call('change', item); });
51986                 }
51987               });
51988           }
51989
51990           // NOTE: Don't change method name until UI v3 is merged
51991           keepRightEditor.error = function(val) {
51992             if (!arguments.length) { return _qaItem; }
51993             _qaItem = val;
51994             return keepRightEditor;
51995           };
51996
51997           return utilRebind(keepRightEditor, dispatch$1, 'on');
51998         }
51999
52000         function uiOsmoseDetails(context) {
52001           var _qaItem;
52002
52003           function issueString(d, type) {
52004             if (!d) { return ''; }
52005
52006             // Issue strings are cached from Osmose API
52007             var s = services.osmose.getStrings(d.itemType);
52008             return (type in s) ? s[type] : '';
52009           }
52010
52011
52012           function osmoseDetails(selection) {
52013             var details = selection.selectAll('.error-details')
52014               .data(
52015                 _qaItem ? [_qaItem] : [],
52016                 function (d) { return ((d.id) + "-" + (d.status || 0)); }
52017               );
52018
52019             details.exit()
52020               .remove();
52021
52022             var detailsEnter = details.enter()
52023               .append('div')
52024                 .attr('class', 'error-details qa-details-container');
52025
52026
52027             // Description
52028             if (issueString(_qaItem, 'detail')) {
52029               var div = detailsEnter
52030                 .append('div')
52031                   .attr('class', 'qa-details-subsection');
52032
52033               div
52034                 .append('h4')
52035                   .text(function () { return _t('QA.keepRight.detail_description'); });
52036
52037               div
52038                 .append('p')
52039                   .attr('class', 'qa-details-description-text')
52040                   .html(function (d) { return issueString(d, 'detail'); })
52041                 .selectAll('a')
52042                   .attr('rel', 'noopener')
52043                   .attr('target', '_blank');
52044             }
52045
52046             // Elements (populated later as data is requested)
52047             var detailsDiv = detailsEnter
52048               .append('div')
52049                 .attr('class', 'qa-details-subsection');
52050
52051             var elemsDiv = detailsEnter
52052               .append('div')
52053                 .attr('class', 'qa-details-subsection');
52054
52055             // Suggested Fix (mustn't exist for every issue type)
52056             if (issueString(_qaItem, 'fix')) {
52057               var div$1 = detailsEnter
52058                 .append('div')
52059                   .attr('class', 'qa-details-subsection');
52060
52061               div$1
52062                 .append('h4')
52063                   .text(function () { return _t('QA.osmose.fix_title'); });
52064
52065               div$1
52066                 .append('p')
52067                   .html(function (d) { return issueString(d, 'fix'); })
52068                 .selectAll('a')
52069                   .attr('rel', 'noopener')
52070                   .attr('target', '_blank');
52071             }
52072
52073             // Common Pitfalls (mustn't exist for every issue type)
52074             if (issueString(_qaItem, 'trap')) {
52075               var div$2 = detailsEnter
52076                 .append('div')
52077                   .attr('class', 'qa-details-subsection');
52078
52079               div$2
52080                 .append('h4')
52081                   .text(function () { return _t('QA.osmose.trap_title'); });
52082
52083               div$2
52084                 .append('p')
52085                   .html(function (d) { return issueString(d, 'trap'); })
52086                 .selectAll('a')
52087                   .attr('rel', 'noopener')
52088                   .attr('target', '_blank');
52089             }
52090
52091             // Save current item to check if UI changed by time request resolves
52092             var thisItem = _qaItem;
52093             services.osmose.loadIssueDetail(_qaItem)
52094               .then(function (d) {
52095                 // No details to add if there are no associated issue elements
52096                 if (!d.elems || d.elems.length === 0) { return; }
52097
52098                 // Do nothing if UI has moved on by the time this resolves
52099                 if (
52100                   context.selectedErrorID() !== thisItem.id
52101                   && context.container().selectAll((".qaItem.osmose.hover.itemId-" + (thisItem.id))).empty()
52102                 ) { return; }
52103
52104                 // Things like keys and values are dynamically added to a subtitle string
52105                 if (d.detail) {
52106                   detailsDiv
52107                     .append('h4')
52108                       .text(function () { return _t('QA.osmose.detail_title'); });
52109
52110                   detailsDiv
52111                     .append('p')
52112                       .html(function (d) { return d.detail; })
52113                     .selectAll('a')
52114                       .attr('rel', 'noopener')
52115                       .attr('target', '_blank');
52116                 }
52117
52118                 // Create list of linked issue elements
52119                 elemsDiv
52120                   .append('h4')
52121                     .text(function () { return _t('QA.osmose.elems_title'); });
52122
52123                 elemsDiv
52124                   .append('ul').selectAll('li')
52125                   .data(d.elems)
52126                   .enter()
52127                   .append('li')
52128                   .append('a')
52129                     .attr('class', 'error_entity_link')
52130                     .text(function (d) { return d; })
52131                     .each(function() {
52132                       var link = select(this);
52133                       var entityID = this.textContent;
52134                       var entity = context.hasEntity(entityID);
52135
52136                       // Add click handler
52137                       link
52138                         .on('mouseenter', function () {
52139                           utilHighlightEntities([entityID], true, context);
52140                         })
52141                         .on('mouseleave', function () {
52142                           utilHighlightEntities([entityID], false, context);
52143                         })
52144                         .on('click', function () {
52145                           event.preventDefault();
52146
52147                           utilHighlightEntities([entityID], false, context);
52148
52149                           var osmlayer = context.layers().layer('osm');
52150                           if (!osmlayer.enabled()) {
52151                             osmlayer.enabled(true);
52152                           }
52153
52154                           context.map().centerZoom(d.loc, 20);
52155
52156                           if (entity) {
52157                             context.enter(modeSelect(context, [entityID]));
52158                           } else {
52159                             context.loadEntity(entityID, function () {
52160                               context.enter(modeSelect(context, [entityID]));
52161                             });
52162                           }
52163                         });
52164
52165                       // Replace with friendly name if possible
52166                       // (The entity may not yet be loaded into the graph)
52167                       if (entity) {
52168                         var name = utilDisplayName(entity);  // try to use common name
52169
52170                         if (!name) {
52171                           var preset = _mainPresetIndex.match(entity, context.graph());
52172                           name = preset && !preset.isFallback() && preset.name();  // fallback to preset name
52173                         }
52174
52175                         if (name) {
52176                           this.innerText = name;
52177                         }
52178                       }
52179                     });
52180
52181                 // Don't hide entities related to this issue - #5880
52182                 context.features().forceVisible(d.elems);
52183                 context.map().pan([0,0]);  // trigger a redraw
52184               })
52185               .catch(function (err) {
52186                 console.log(err); // eslint-disable-line no-console
52187               });
52188           }
52189
52190
52191           osmoseDetails.issue = function(val) {
52192             if (!arguments.length) { return _qaItem; }
52193             _qaItem = val;
52194             return osmoseDetails;
52195           };
52196
52197
52198           return osmoseDetails;
52199         }
52200
52201         function uiOsmoseHeader() {
52202           var _qaItem;
52203
52204           function issueTitle(d) {
52205             var unknown = _t('inspector.unknown');
52206
52207             if (!d) { return unknown; }
52208
52209             // Issue titles supplied by Osmose
52210             var s = services.osmose.getStrings(d.itemType);
52211             return ('title' in s) ? s.title : unknown;
52212           }
52213
52214           function osmoseHeader(selection) {
52215             var header = selection.selectAll('.qa-header')
52216               .data(
52217                 (_qaItem ? [_qaItem] : []),
52218                 function (d) { return ((d.id) + "-" + (d.status || 0)); }
52219               );
52220
52221             header.exit()
52222               .remove();
52223
52224             var headerEnter = header.enter()
52225               .append('div')
52226                 .attr('class', 'qa-header');
52227
52228             var svgEnter = headerEnter
52229               .append('div')
52230                 .attr('class', 'qa-header-icon')
52231                 .classed('new', function (d) { return d.id < 0; })
52232               .append('svg')
52233                 .attr('width', '20px')
52234                 .attr('height', '30px')
52235                 .attr('viewbox', '0 0 20 30')
52236                 .attr('class', function (d) { return ("preset-icon-28 qaItem " + (d.service) + " itemId-" + (d.id) + " itemType-" + (d.itemType)); });
52237
52238             svgEnter
52239               .append('polygon')
52240                 .attr('fill', function (d) { return services.osmose.getColor(d.item); })
52241                 .attr('class', 'qaItem-fill')
52242                 .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');
52243
52244             svgEnter
52245               .append('use')
52246                 .attr('class', 'icon-annotation')
52247                 .attr('width', '13px')
52248                 .attr('height', '13px')
52249                 .attr('transform', 'translate(3.5, 5)')
52250                 .attr('xlink:href', function (d) {
52251                   var picon = d.icon;
52252
52253                   if (!picon) {
52254                     return '';
52255                   } else {
52256                     var isMaki = /^maki-/.test(picon);
52257                     return ("#" + picon + (isMaki ? '-11' : ''));
52258                   }
52259                 });
52260
52261             headerEnter
52262               .append('div')
52263                 .attr('class', 'qa-header-label')
52264                 .text(issueTitle);
52265           }
52266
52267           osmoseHeader.issue = function(val) {
52268             if (!arguments.length) { return _qaItem; }
52269             _qaItem = val;
52270             return osmoseHeader;
52271           };
52272
52273           return osmoseHeader;
52274         }
52275
52276         function uiViewOnOsmose() {
52277           var _qaItem;
52278
52279           function viewOnOsmose(selection) {
52280             var url;
52281             if (services.osmose && (_qaItem instanceof QAItem)) {
52282               url = services.osmose.itemURL(_qaItem);
52283             }
52284
52285             var link = selection.selectAll('.view-on-osmose')
52286               .data(url ? [url] : []);
52287
52288             // exit
52289             link.exit()
52290               .remove();
52291
52292             // enter
52293             var linkEnter = link.enter()
52294               .append('a')
52295                 .attr('class', 'view-on-osmose')
52296                 .attr('target', '_blank')
52297                 .attr('rel', 'noopener') // security measure
52298                 .attr('href', function (d) { return d; })
52299                 .call(svgIcon('#iD-icon-out-link', 'inline'));
52300
52301             linkEnter
52302               .append('span')
52303                 .text(_t('inspector.view_on_osmose'));
52304           }
52305
52306           viewOnOsmose.what = function(val) {
52307             if (!arguments.length) { return _qaItem; }
52308             _qaItem = val;
52309             return viewOnOsmose;
52310           };
52311
52312           return viewOnOsmose;
52313         }
52314
52315         function uiOsmoseEditor(context) {
52316           var dispatch$1 = dispatch('change');
52317           var qaDetails = uiOsmoseDetails(context);
52318           var qaHeader = uiOsmoseHeader();
52319
52320           var _qaItem;
52321
52322           function osmoseEditor(selection) {
52323
52324             var header = selection.selectAll('.header')
52325               .data([0]);
52326
52327             var headerEnter = header.enter()
52328               .append('div')
52329                 .attr('class', 'header fillL');
52330
52331             headerEnter
52332               .append('button')
52333                 .attr('class', 'close')
52334                 .on('click', function () { return context.enter(modeBrowse(context)); })
52335                 .call(svgIcon('#iD-icon-close'));
52336
52337             headerEnter
52338               .append('h3')
52339                 .text(_t('QA.osmose.title'));
52340
52341             var body = selection.selectAll('.body')
52342               .data([0]);
52343
52344             body = body.enter()
52345                 .append('div')
52346                 .attr('class', 'body')
52347               .merge(body);
52348
52349             var editor = body.selectAll('.qa-editor')
52350               .data([0]);
52351
52352             editor.enter()
52353               .append('div')
52354                 .attr('class', 'modal-section qa-editor')
52355               .merge(editor)
52356                 .call(qaHeader.issue(_qaItem))
52357                 .call(qaDetails.issue(_qaItem))
52358                 .call(osmoseSaveSection);
52359
52360             var footer = selection.selectAll('.footer')
52361               .data([0]);
52362
52363             footer.enter()
52364               .append('div')
52365               .attr('class', 'footer')
52366               .merge(footer)
52367               .call(uiViewOnOsmose().what(_qaItem));
52368           }
52369
52370           function osmoseSaveSection(selection) {
52371             var isSelected = (_qaItem && _qaItem.id === context.selectedErrorID());
52372             var isShown = (_qaItem && isSelected);
52373             var saveSection = selection.selectAll('.qa-save')
52374               .data(
52375                 (isShown ? [_qaItem] : []),
52376                 function (d) { return ((d.id) + "-" + (d.status || 0)); }
52377               );
52378
52379             // exit
52380             saveSection.exit()
52381               .remove();
52382
52383             // enter
52384             var saveSectionEnter = saveSection.enter()
52385               .append('div')
52386                 .attr('class', 'qa-save save-section cf');
52387
52388             // update
52389             saveSection = saveSectionEnter
52390               .merge(saveSection)
52391                 .call(qaSaveButtons);
52392           }
52393
52394           function qaSaveButtons(selection) {
52395             var isSelected = (_qaItem && _qaItem.id === context.selectedErrorID());
52396             var buttonSection = selection.selectAll('.buttons')
52397               .data((isSelected ? [_qaItem] : []), function (d) { return d.status + d.id; });
52398
52399             // exit
52400             buttonSection.exit()
52401               .remove();
52402
52403             // enter
52404             var buttonEnter = buttonSection.enter()
52405               .append('div')
52406                 .attr('class', 'buttons');
52407
52408             buttonEnter
52409               .append('button')
52410                 .attr('class', 'button close-button action');
52411
52412             buttonEnter
52413               .append('button')
52414                 .attr('class', 'button ignore-button action');
52415
52416             // update
52417             buttonSection = buttonSection
52418               .merge(buttonEnter);
52419
52420             buttonSection.select('.close-button')
52421               .text(function () { return _t('QA.keepRight.close'); })
52422               .on('click.close', function(d) {
52423                 this.blur();    // avoid keeping focus on the button - #4641
52424                 var qaService = services.osmose;
52425                 if (qaService) {
52426                   d.newStatus = 'done';
52427                   qaService.postUpdate(d, function (err, item) { return dispatch$1.call('change', item); });
52428                 }
52429               });
52430
52431             buttonSection.select('.ignore-button')
52432               .text(function () { return _t('QA.keepRight.ignore'); })
52433               .on('click.ignore', function(d) {
52434                 this.blur();    // avoid keeping focus on the button - #4641
52435                 var qaService = services.osmose;
52436                 if (qaService) {
52437                   d.newStatus = 'false';
52438                   qaService.postUpdate(d, function (err, item) { return dispatch$1.call('change', item); });
52439                 }
52440               });
52441           }
52442
52443           // NOTE: Don't change method name until UI v3 is merged
52444           osmoseEditor.error = function(val) {
52445             if (!arguments.length) { return _qaItem; }
52446             _qaItem = val;
52447             return osmoseEditor;
52448           };
52449
52450           return utilRebind(osmoseEditor, dispatch$1, 'on');
52451         }
52452
52453         // NOTE: Don't change name of this until UI v3 is merged
52454         function modeSelectError(context, selectedErrorID, selectedErrorService) {
52455             var mode = {
52456                 id: 'select-error',
52457                 button: 'browse'
52458             };
52459
52460             var keybinding = utilKeybinding('select-error');
52461
52462             var errorService = services[selectedErrorService];
52463             var errorEditor;
52464             switch (selectedErrorService) {
52465                 case 'improveOSM':
52466                     errorEditor = uiImproveOsmEditor(context)
52467                     .on('change', function() {
52468                         context.map().pan([0,0]);  // trigger a redraw
52469                         var error = checkSelectedID();
52470                         if (!error) { return; }
52471                         context.ui().sidebar
52472                             .show(errorEditor.error(error));
52473                     });
52474                     break;
52475                 case 'keepRight':
52476                     errorEditor = uiKeepRightEditor(context)
52477                     .on('change', function() {
52478                         context.map().pan([0,0]);  // trigger a redraw
52479                         var error = checkSelectedID();
52480                         if (!error) { return; }
52481                         context.ui().sidebar
52482                             .show(errorEditor.error(error));
52483                     });
52484                     break;
52485                 case 'osmose':
52486                     errorEditor = uiOsmoseEditor(context)
52487                     .on('change', function() {
52488                         context.map().pan([0,0]);  // trigger a redraw
52489                         var error = checkSelectedID();
52490                         if (!error) { return; }
52491                         context.ui().sidebar
52492                             .show(errorEditor.error(error));
52493                     });
52494                     break;
52495             }
52496
52497
52498             var behaviors = [
52499                 behaviorBreathe(),
52500                 behaviorHover(context),
52501                 behaviorSelect(context),
52502                 behaviorLasso(context),
52503                 modeDragNode(context).behavior,
52504                 modeDragNote(context).behavior
52505             ];
52506
52507
52508             function checkSelectedID() {
52509                 if (!errorService) { return; }
52510                 var error = errorService.getError(selectedErrorID);
52511                 if (!error) {
52512                     context.enter(modeBrowse(context));
52513                 }
52514                 return error;
52515             }
52516
52517
52518             mode.zoomToSelected = function() {
52519                 if (!errorService) { return; }
52520                 var error = errorService.getError(selectedErrorID);
52521                 if (error) {
52522                     context.map().centerZoomEase(error.loc, 20);
52523                 }
52524             };
52525
52526
52527             mode.enter = function() {
52528                 var error = checkSelectedID();
52529                 if (!error) { return; }
52530
52531                 behaviors.forEach(context.install);
52532                 keybinding
52533                     .on(_t('inspector.zoom_to.key'), mode.zoomToSelected)
52534                     .on('⎋', esc, true);
52535
52536                 select(document)
52537                     .call(keybinding);
52538
52539                 selectError();
52540
52541                 var sidebar = context.ui().sidebar;
52542                 sidebar.show(errorEditor.error(error));
52543
52544                 context.map()
52545                     .on('drawn.select-error', selectError);
52546
52547
52548                 // class the error as selected, or return to browse mode if the error is gone
52549                 function selectError(drawn) {
52550                     if (!checkSelectedID()) { return; }
52551
52552                     var selection = context.surface()
52553                         .selectAll('.itemId-' + selectedErrorID + '.' + selectedErrorService);
52554
52555                     if (selection.empty()) {
52556                         // Return to browse mode if selected DOM elements have
52557                         // disappeared because the user moved them out of view..
52558                         var source = event && event.type === 'zoom' && event.sourceEvent;
52559                         if (drawn && source && (source.type === 'pointermove' || source.type === 'mousemove' || source.type === 'touchmove')) {
52560                             context.enter(modeBrowse(context));
52561                         }
52562
52563                     } else {
52564                         selection
52565                             .classed('selected', true);
52566
52567                         context.selectedErrorID(selectedErrorID);
52568                     }
52569                 }
52570
52571                 function esc() {
52572                     if (context.container().select('.combobox').size()) { return; }
52573                     context.enter(modeBrowse(context));
52574                 }
52575             };
52576
52577
52578             mode.exit = function() {
52579                 behaviors.forEach(context.uninstall);
52580
52581                 select(document)
52582                     .call(keybinding.unbind);
52583
52584                 context.surface()
52585                     .selectAll('.qaItem.selected')
52586                     .classed('selected hover', false);
52587
52588                 context.map()
52589                     .on('drawn.select-error', null);
52590
52591                 context.ui().sidebar
52592                     .hide();
52593
52594                 context.selectedErrorID(null);
52595                 context.features().forceVisible([]);
52596             };
52597
52598
52599             return mode;
52600         }
52601
52602         function behaviorSelect(context) {
52603             var _tolerancePx = 4; // see also behaviorDrag
52604             var _lastMouseEvent = null;
52605             var _showMenu = false;
52606             var _downPointers = {};
52607             var _longPressTimeout = null;
52608             var _lastInteractionType = null;
52609             // the id of the down pointer that's enabling multiselection while down
52610             var _multiselectionPointerId = null;
52611
52612             // use pointer events on supported platforms; fallback to mouse events
52613             var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
52614
52615
52616             function keydown() {
52617
52618                 if (event.keyCode === 32) {
52619                     // don't react to spacebar events during text input
52620                     var activeNode = document.activeElement;
52621                     if (activeNode && new Set(['INPUT', 'TEXTAREA']).has(activeNode.nodeName)) { return; }
52622                 }
52623
52624                 if (event.keyCode === 93 ||  // context menu key
52625                     event.keyCode === 32) {  // spacebar
52626                     event.preventDefault();
52627                 }
52628
52629                 if (event.repeat) { return; } // ignore repeated events for held keys
52630
52631                 // if any key is pressed the user is probably doing something other than long-pressing
52632                 cancelLongPress();
52633
52634                 if (event.shiftKey) {
52635                     context.surface()
52636                         .classed('behavior-multiselect', true);
52637                 }
52638
52639                 if (event.keyCode === 32) {  // spacebar
52640                     if (!_downPointers.spacebar && _lastMouseEvent) {
52641                         cancelLongPress();
52642                         _longPressTimeout = window.setTimeout(didLongPress, 500, 'spacebar', 'spacebar');
52643
52644                         _downPointers.spacebar = {
52645                             firstEvent: _lastMouseEvent,
52646                             lastEvent: _lastMouseEvent
52647                         };
52648                     }
52649                 }
52650             }
52651
52652
52653             function keyup() {
52654                 cancelLongPress();
52655
52656                 if (!event.shiftKey) {
52657                     context.surface()
52658                         .classed('behavior-multiselect', false);
52659                 }
52660
52661                 if (event.keyCode === 93) {  // context menu key
52662                     event.preventDefault();
52663                     _lastInteractionType = 'menukey';
52664                     contextmenu();
52665                 } else if (event.keyCode === 32) {  // spacebar
52666                     var pointer = _downPointers.spacebar;
52667                     if (pointer) {
52668                         delete _downPointers.spacebar;
52669
52670                         if (pointer.done) { return; }
52671
52672                         event.preventDefault();
52673                         _lastInteractionType = 'spacebar';
52674                         click(pointer.firstEvent, pointer.lastEvent, 'spacebar');
52675                     }
52676                 }
52677             }
52678
52679
52680             function pointerdown() {
52681                 var id = (event.pointerId || 'mouse').toString();
52682
52683                 cancelLongPress();
52684
52685                 if (event.buttons && event.buttons !== 1) { return; }
52686
52687                 context.ui().closeEditMenu();
52688
52689                 _longPressTimeout = window.setTimeout(didLongPress, 500, id, 'longdown-' + (event.pointerType || 'mouse'));
52690
52691                 _downPointers[id] = {
52692                     firstEvent: event,
52693                     lastEvent: event
52694                 };
52695             }
52696
52697
52698             function didLongPress(id, interactionType) {
52699                 var pointer = _downPointers[id];
52700                 if (!pointer) { return; }
52701
52702                 for (var i in _downPointers) {
52703                     // don't allow this or any currently down pointer to trigger another click
52704                     _downPointers[i].done = true;
52705                 }
52706
52707                 // treat long presses like right-clicks
52708                 _longPressTimeout = null;
52709                 _lastInteractionType = interactionType;
52710                 _showMenu = true;
52711
52712                 click(pointer.firstEvent, pointer.lastEvent, id);
52713             }
52714
52715
52716             function pointermove() {
52717                 var id = (event.pointerId || 'mouse').toString();
52718                 if (_downPointers[id]) {
52719                     _downPointers[id].lastEvent = event;
52720                 }
52721                 if (!event.pointerType || event.pointerType === 'mouse') {
52722                     _lastMouseEvent = event;
52723                     if (_downPointers.spacebar) {
52724                         _downPointers.spacebar.lastEvent = event;
52725                     }
52726                 }
52727             }
52728
52729
52730             function pointerup() {
52731                 var id = (event.pointerId || 'mouse').toString();
52732                 var pointer = _downPointers[id];
52733                 if (!pointer) { return; }
52734
52735                 delete _downPointers[id];
52736
52737                 if (_multiselectionPointerId === id) {
52738                     _multiselectionPointerId = null;
52739                 }
52740
52741                 if (pointer.done) { return; }
52742
52743                 click(pointer.firstEvent, event, id);
52744             }
52745
52746
52747             function pointercancel() {
52748                 var id = (event.pointerId || 'mouse').toString();
52749                 if (!_downPointers[id]) { return; }
52750
52751                 delete _downPointers[id];
52752
52753                 if (_multiselectionPointerId === id) {
52754                     _multiselectionPointerId = null;
52755                 }
52756             }
52757
52758
52759             function contextmenu() {
52760                 var e = event;
52761                 e.preventDefault();
52762
52763                 if (!+e.clientX && !+e.clientY) {
52764                     if (_lastMouseEvent) {
52765                         e.sourceEvent = _lastMouseEvent;
52766                     } else {
52767                         return;
52768                     }
52769                 } else {
52770                     _lastMouseEvent = event;
52771                     _lastInteractionType = 'rightclick';
52772                 }
52773
52774                 _showMenu = true;
52775                 click(event, event);
52776             }
52777
52778
52779             function click(firstEvent, lastEvent, pointerId) {
52780                 cancelLongPress();
52781
52782                 var mapNode = context.container().select('.main-map').node();
52783
52784                 // Use the `main-map` coordinate system since the surface and supersurface
52785                 // are transformed when drag-panning.
52786                 var pointGetter = utilFastMouse(mapNode);
52787                 var p1 = pointGetter(firstEvent);
52788                 var p2 = pointGetter(lastEvent);
52789                 var dist = geoVecLength(p1, p2);
52790
52791                 if (dist > _tolerancePx ||
52792                     !mapContains(lastEvent)) {
52793
52794                     resetProperties();
52795                     return;
52796                 }
52797
52798                 var targetDatum = lastEvent.target.__data__;
52799
52800                 var multiselectEntityId;
52801
52802                 if (!_multiselectionPointerId) {
52803                     // If a different pointer than the one triggering this click is down on a
52804                     // feature, treat this and all future clicks as multiselection until that
52805                     // pointer is raised.
52806                     var selectPointerInfo = pointerDownOnSelection(pointerId);
52807                     if (selectPointerInfo) {
52808                         _multiselectionPointerId = selectPointerInfo.pointerId;
52809                         // if the other feature isn't selected yet, make sure we select it
52810                         multiselectEntityId = !selectPointerInfo.selected && selectPointerInfo.entityId;
52811                         _downPointers[selectPointerInfo.pointerId].done = true;
52812                     }
52813                 }
52814
52815                 // support multiselect if data is already selected
52816                 var isMultiselect = context.mode().id === 'select' && (
52817                     // and shift key is down
52818                     (event && event.shiftKey) ||
52819                     // or we're lasso-selecting
52820                     context.surface().select('.lasso').node() ||
52821                     // or a pointer is down over a selected feature
52822                     (_multiselectionPointerId && !multiselectEntityId)
52823                 );
52824
52825                 processClick(targetDatum, isMultiselect, p2, multiselectEntityId);
52826
52827                 function mapContains(event) {
52828                     var rect = mapNode.getBoundingClientRect();
52829                     return event.clientX >= rect.left &&
52830                         event.clientX <= rect.right &&
52831                         event.clientY >= rect.top &&
52832                         event.clientY <= rect.bottom;
52833                 }
52834
52835                 function pointerDownOnSelection(skipPointerId) {
52836                     var mode = context.mode();
52837                     var selectedIDs = mode.id === 'select' ? mode.selectedIDs() : [];
52838                     for (var pointerId in _downPointers) {
52839                         if (pointerId === 'spacebar' || pointerId === skipPointerId) { continue; }
52840
52841                         var pointerInfo = _downPointers[pointerId];
52842
52843                         var p1 = pointGetter(pointerInfo.firstEvent);
52844                         var p2 = pointGetter(pointerInfo.lastEvent);
52845                         if (geoVecLength(p1, p2) > _tolerancePx) { continue; }
52846
52847                         var datum = pointerInfo.firstEvent.target.__data__;
52848                         var entity = (datum && datum.properties && datum.properties.entity) || datum;
52849                         if (context.graph().hasEntity(entity.id)) { return {
52850                             pointerId: pointerId,
52851                             entityId: entity.id,
52852                             selected: selectedIDs.indexOf(entity.id) !== -1
52853                         }; }
52854                     }
52855                     return null;
52856                 }
52857             }
52858
52859
52860             function processClick(datum, isMultiselect, point, alsoSelectId) {
52861                 var mode = context.mode();
52862                 var showMenu = _showMenu;
52863                 var interactionType = _lastInteractionType;
52864
52865                 var entity = datum && datum.properties && datum.properties.entity;
52866                 if (entity) { datum = entity; }
52867
52868                 if (datum && datum.type === 'midpoint') {
52869                     // treat targeting midpoints as if targeting the parent way
52870                     datum = datum.parents[0];
52871                 }
52872
52873                 var newMode;
52874
52875                 if (datum instanceof osmEntity) {
52876                     // targeting an entity
52877                     var selectedIDs = context.selectedIDs();
52878                     context.selectedNoteID(null);
52879                     context.selectedErrorID(null);
52880
52881                     if (!isMultiselect) {
52882                         // don't change the selection if we're toggling the menu atop a multiselection
52883                         if (!showMenu ||
52884                             selectedIDs.length <= 1 ||
52885                             selectedIDs.indexOf(datum.id) === -1) {
52886
52887                             if (alsoSelectId === datum.id) { alsoSelectId = null; }
52888
52889                             selectedIDs = (alsoSelectId ? [alsoSelectId] : []).concat([datum.id]);
52890                             // always enter modeSelect even if the entity is already
52891                             // selected since listeners may expect `context.enter` events,
52892                             // e.g. in the walkthrough
52893                             newMode = mode.id === 'select' ? mode.selectedIDs(selectedIDs) : modeSelect(context, selectedIDs).selectBehavior(behavior);
52894                             context.enter(newMode);
52895                         }
52896
52897                     } else {
52898                         if (selectedIDs.indexOf(datum.id) !== -1) {
52899                             // clicked entity is already in the selectedIDs list..
52900                             if (!showMenu) {
52901                                 // deselect clicked entity, then reenter select mode or return to browse mode..
52902                                 selectedIDs = selectedIDs.filter(function(id) { return id !== datum.id; });
52903                                 newMode = selectedIDs.length ? mode.selectedIDs(selectedIDs) : modeBrowse(context).selectBehavior(behavior);
52904                                 context.enter(newMode);
52905                             }
52906                         } else {
52907                             // clicked entity is not in the selected list, add it..
52908                             selectedIDs = selectedIDs.concat([datum.id]);
52909                             newMode = mode.selectedIDs(selectedIDs);
52910                             context.enter(newMode);
52911                         }
52912                     }
52913
52914                 } else if (datum && datum.__featurehash__ && !isMultiselect) {
52915                     // targeting custom data
52916                     context
52917                         .selectedNoteID(null)
52918                         .enter(modeSelectData(context, datum));
52919
52920                 } else if (datum instanceof osmNote && !isMultiselect) {
52921                     // targeting a note
52922                     context
52923                         .selectedNoteID(datum.id)
52924                         .enter(modeSelectNote(context, datum.id));
52925
52926                 } else if (datum instanceof QAItem & !isMultiselect) {
52927                     // targeting an external QA issue
52928                     context
52929                         .selectedErrorID(datum.id)
52930                         .enter(modeSelectError(context, datum.id, datum.service));
52931
52932                 } else {
52933                     // targeting nothing
52934                     context.selectedNoteID(null);
52935                     context.selectedErrorID(null);
52936                     if (!isMultiselect && mode.id !== 'browse') {
52937                         context.enter(modeBrowse(context));
52938                     }
52939                 }
52940
52941                 context.ui().closeEditMenu();
52942
52943                 // always request to show the edit menu in case the mode needs it
52944                 if (showMenu) { context.ui().showEditMenu(point, interactionType); }
52945
52946                 resetProperties();
52947             }
52948
52949
52950             function cancelLongPress() {
52951                 if (_longPressTimeout) { window.clearTimeout(_longPressTimeout); }
52952                 _longPressTimeout = null;
52953             }
52954
52955
52956             function resetProperties() {
52957                 cancelLongPress();
52958                 _showMenu = false;
52959                 _lastInteractionType = null;
52960                 // don't reset _lastMouseEvent since it might still be useful
52961             }
52962
52963
52964             function behavior(selection) {
52965                 resetProperties();
52966                 _lastMouseEvent = context.map().lastPointerEvent();
52967
52968                 select(window)
52969                     .on('keydown.select', keydown)
52970                     .on('keyup.select', keyup)
52971                     .on(_pointerPrefix + 'move.select', pointermove, true)
52972                     .on(_pointerPrefix + 'up.select', pointerup, true)
52973                     .on('pointercancel.select', pointercancel, true)
52974                     .on('contextmenu.select-window', function() {
52975                         // Edge and IE really like to show the contextmenu on the
52976                         // menubar when user presses a keyboard menu button
52977                         // even after we've already preventdefaulted the key event.
52978                         var e = event;
52979                         if (+e.clientX === 0 && +e.clientY === 0) {
52980                             event.preventDefault();
52981                         }
52982                     });
52983
52984                 selection
52985                     .on(_pointerPrefix + 'down.select', pointerdown)
52986                     .on('contextmenu.select', contextmenu);
52987
52988                 if (event && event.shiftKey) {
52989                     context.surface()
52990                         .classed('behavior-multiselect', true);
52991                 }
52992             }
52993
52994
52995             behavior.off = function(selection) {
52996                 cancelLongPress();
52997
52998                 select(window)
52999                     .on('keydown.select', null)
53000                     .on('keyup.select', null)
53001                     .on('contextmenu.select-window', null)
53002                     .on(_pointerPrefix + 'move.select', null, true)
53003                     .on(_pointerPrefix + 'up.select', null, true)
53004                     .on('pointercancel.select', null, true);
53005
53006                 selection
53007                     .on(_pointerPrefix + 'down.select', null)
53008                     .on('contextmenu.select', null);
53009
53010                 context.surface()
53011                     .classed('behavior-multiselect', false);
53012             };
53013
53014
53015             return behavior;
53016         }
53017
53018         function behaviorDrawWay(context, wayID, mode, startGraph) {
53019
53020             var dispatch$1 = dispatch('rejectedSelfIntersection');
53021
53022             var behavior = behaviorDraw(context);
53023
53024             // Must be set by `drawWay.nodeIndex` before each install of this behavior.
53025             var _nodeIndex;
53026
53027             var _origWay;
53028             var _wayGeometry;
53029             var _headNodeID;
53030             var _annotation;
53031
53032             var _pointerHasMoved = false;
53033
53034             // The osmNode to be placed.
53035             // This is temporary and just follows the mouse cursor until an "add" event occurs.
53036             var _drawNode;
53037
53038             var _didResolveTempEdit = false;
53039
53040             function createDrawNode(loc) {
53041                 // don't make the draw node until we actually need it
53042                 _drawNode = osmNode({ loc: loc });
53043
53044                 context.pauseChangeDispatch();
53045                 context.replace(function actionAddDrawNode(graph) {
53046                     // add the draw node to the graph and insert it into the way
53047                     var way = graph.entity(wayID);
53048                     return graph
53049                         .replace(_drawNode)
53050                         .replace(way.addNode(_drawNode.id, _nodeIndex));
53051                 }, _annotation);
53052                 context.resumeChangeDispatch();
53053
53054                 setActiveElements();
53055             }
53056
53057             function removeDrawNode() {
53058
53059                 context.pauseChangeDispatch();
53060                 context.replace(
53061                     function actionDeleteDrawNode(graph) {
53062                        var way = graph.entity(wayID);
53063                        return graph
53064                            .replace(way.removeNode(_drawNode.id))
53065                            .remove(_drawNode);
53066                    },
53067                     _annotation
53068                 );
53069                 _drawNode = undefined;
53070                 context.resumeChangeDispatch();
53071             }
53072
53073
53074             function keydown() {
53075                 if (event.keyCode === utilKeybinding.modifierCodes.alt) {
53076                     if (context.surface().classed('nope')) {
53077                         context.surface()
53078                             .classed('nope-suppressed', true);
53079                     }
53080                     context.surface()
53081                         .classed('nope', false)
53082                         .classed('nope-disabled', true);
53083                 }
53084             }
53085
53086
53087             function keyup() {
53088                 if (event.keyCode === utilKeybinding.modifierCodes.alt) {
53089                     if (context.surface().classed('nope-suppressed')) {
53090                         context.surface()
53091                             .classed('nope', true);
53092                     }
53093                     context.surface()
53094                         .classed('nope-suppressed', false)
53095                         .classed('nope-disabled', false);
53096                 }
53097             }
53098
53099
53100             function allowsVertex(d) {
53101                 return d.geometry(context.graph()) === 'vertex' || _mainPresetIndex.allowsVertex(d, context.graph());
53102             }
53103
53104
53105             // related code
53106             // - `mode/drag_node.js`     `doMove()`
53107             // - `behavior/draw.js`      `click()`
53108             // - `behavior/draw_way.js`  `move()`
53109             function move(datum) {
53110
53111                 var loc = context.map().mouseCoordinates();
53112
53113                 if (!_drawNode) { createDrawNode(loc); }
53114
53115                 context.surface().classed('nope-disabled', event.altKey);
53116
53117                 var targetLoc = datum && datum.properties && datum.properties.entity &&
53118                     allowsVertex(datum.properties.entity) && datum.properties.entity.loc;
53119                 var targetNodes = datum && datum.properties && datum.properties.nodes;
53120
53121                 if (targetLoc) {   // snap to node/vertex - a point target with `.loc`
53122                     loc = targetLoc;
53123
53124                 } else if (targetNodes) {   // snap to way - a line target with `.nodes`
53125                     var choice = geoChooseEdge(targetNodes, context.map().mouse(), context.projection, _drawNode.id);
53126                     if (choice) {
53127                         loc = choice.loc;
53128                     }
53129                 }
53130
53131                 context.replace(actionMoveNode(_drawNode.id, loc), _annotation);
53132                 _drawNode = context.entity(_drawNode.id);
53133                 checkGeometry(true /* includeDrawNode */);
53134             }
53135
53136
53137             // Check whether this edit causes the geometry to break.
53138             // If so, class the surface with a nope cursor.
53139             // `includeDrawNode` - Only check the relevant line segments if finishing drawing
53140             function checkGeometry(includeDrawNode) {
53141                 var nopeDisabled = context.surface().classed('nope-disabled');
53142                 var isInvalid = isInvalidGeometry(includeDrawNode);
53143
53144                 if (nopeDisabled) {
53145                     context.surface()
53146                         .classed('nope', false)
53147                         .classed('nope-suppressed', isInvalid);
53148                 } else {
53149                     context.surface()
53150                         .classed('nope', isInvalid)
53151                         .classed('nope-suppressed', false);
53152                 }
53153             }
53154
53155
53156             function isInvalidGeometry(includeDrawNode) {
53157
53158                 var testNode = _drawNode;
53159
53160                 // we only need to test the single way we're drawing
53161                 var parentWay = context.graph().entity(wayID);
53162                 var nodes = context.graph().childNodes(parentWay).slice();  // shallow copy
53163
53164                 if (includeDrawNode) {
53165                     if (parentWay.isClosed()) {
53166                         // don't test the last segment for closed ways - #4655
53167                         // (still test the first segment)
53168                         nodes.pop();
53169                     }
53170                 } else { // discount the draw node
53171
53172                     if (parentWay.isClosed()) {
53173                         if (nodes.length < 3) { return false; }
53174                         if (_drawNode) { nodes.splice(-2, 1); }
53175                         testNode = nodes[nodes.length - 2];
53176                     } else {
53177                         // there's nothing we need to test if we ignore the draw node on open ways
53178                         return false;
53179                     }
53180                 }
53181
53182                 return testNode && geoHasSelfIntersections(nodes, testNode.id);
53183             }
53184
53185
53186             function undone() {
53187
53188                 // undoing removed the temp edit
53189                 _didResolveTempEdit = true;
53190
53191                 context.pauseChangeDispatch();
53192
53193                 var nextMode;
53194
53195                 if (context.graph() === startGraph) {
53196                     // We've undone back to the initial state before we started drawing.
53197                     // Just exit the draw mode without undoing whatever we did before
53198                     // we entered the draw mode.
53199                     nextMode = modeSelect(context, [wayID]);
53200                 } else {
53201                     // The `undo` only removed the temporary edit, so here we have to
53202                     // manually undo to actually remove the last node we added. We can't
53203                     // use the `undo` function since the initial "add" graph doesn't have
53204                     // an annotation and so cannot be undone to.
53205                     context.pop(1);
53206
53207                     // continue drawing
53208                     nextMode = mode;
53209                 }
53210
53211                 // clear the redo stack by adding and removing a blank edit
53212                 context.perform(actionNoop());
53213                 context.pop(1);
53214
53215                 context.resumeChangeDispatch();
53216                 context.enter(nextMode);
53217             }
53218
53219
53220             function setActiveElements() {
53221                 if (!_drawNode) { return; }
53222
53223                 context.surface().selectAll('.' + _drawNode.id)
53224                     .classed('active', true);
53225             }
53226
53227
53228             function resetToStartGraph() {
53229                 while (context.graph() !== startGraph) {
53230                     context.pop();
53231                 }
53232             }
53233
53234
53235             var drawWay = function(surface) {
53236                 _drawNode = undefined;
53237                 _didResolveTempEdit = false;
53238                 _origWay = context.entity(wayID);
53239                 _headNodeID = typeof _nodeIndex === 'number' ? _origWay.nodes[_nodeIndex] :
53240                     (_origWay.isClosed() ? _origWay.nodes[_origWay.nodes.length - 2] : _origWay.nodes[_origWay.nodes.length - 1]);
53241                 _wayGeometry = _origWay.geometry(context.graph());
53242                 _annotation = _t((_origWay.nodes.length === (_origWay.isClosed() ? 2 : 1) ?
53243                     'operations.start.annotation.' :
53244                     'operations.continue.annotation.') + _wayGeometry
53245                 );
53246                 _pointerHasMoved = false;
53247
53248                 // Push an annotated state for undo to return back to.
53249                 // We must make sure to replace or remove it later.
53250                 context.pauseChangeDispatch();
53251                 context.perform(actionNoop(), _annotation);
53252                 context.resumeChangeDispatch();
53253
53254                 behavior.hover()
53255                     .initialNodeID(_headNodeID);
53256
53257                 behavior
53258                     .on('move', function() {
53259                         _pointerHasMoved = true;
53260                         move.apply(this, arguments);
53261                     })
53262                     .on('down', function() {
53263                         move.apply(this, arguments);
53264                     })
53265                     .on('downcancel', function() {
53266                         if (_drawNode) { removeDrawNode(); }
53267                     })
53268                     .on('click', drawWay.add)
53269                     .on('clickWay', drawWay.addWay)
53270                     .on('clickNode', drawWay.addNode)
53271                     .on('undo', context.undo)
53272                     .on('cancel', drawWay.cancel)
53273                     .on('finish', drawWay.finish);
53274
53275                 select(window)
53276                     .on('keydown.drawWay', keydown)
53277                     .on('keyup.drawWay', keyup);
53278
53279                 context.map()
53280                     .dblclickZoomEnable(false)
53281                     .on('drawn.draw', setActiveElements);
53282
53283                 setActiveElements();
53284
53285                 surface.call(behavior);
53286
53287                 context.history()
53288                     .on('undone.draw', undone);
53289             };
53290
53291
53292             drawWay.off = function(surface) {
53293
53294                 if (!_didResolveTempEdit) {
53295                     // Drawing was interrupted unexpectedly.
53296                     // This can happen if the user changes modes,
53297                     // clicks geolocate button, a hashchange event occurs, etc.
53298
53299                     context.pauseChangeDispatch();
53300                     resetToStartGraph();
53301                     context.resumeChangeDispatch();
53302                 }
53303
53304                 _drawNode = undefined;
53305                 _nodeIndex = undefined;
53306
53307                 context.map()
53308                     .on('drawn.draw', null);
53309
53310                 surface.call(behavior.off)
53311                     .selectAll('.active')
53312                     .classed('active', false);
53313
53314                 surface
53315                     .classed('nope', false)
53316                     .classed('nope-suppressed', false)
53317                     .classed('nope-disabled', false);
53318
53319                 select(window)
53320                     .on('keydown.drawWay', null)
53321                     .on('keyup.drawWay', null);
53322
53323                 context.history()
53324                     .on('undone.draw', null);
53325             };
53326
53327
53328             function attemptAdd(d, loc, doAdd) {
53329
53330                 if (_drawNode) {
53331                     // move the node to the final loc in case move wasn't called
53332                     // consistently (e.g. on touch devices)
53333                     context.replace(actionMoveNode(_drawNode.id, loc), _annotation);
53334                     _drawNode = context.entity(_drawNode.id);
53335                 } else {
53336                     createDrawNode(loc);
53337                 }
53338
53339                 checkGeometry(true /* includeDrawNode */);
53340                 if ((d && d.properties && d.properties.nope) || context.surface().classed('nope')) {
53341                     if (!_pointerHasMoved) {
53342                         // prevent the temporary draw node from appearing on touch devices
53343                         removeDrawNode();
53344                     }
53345                     dispatch$1.call('rejectedSelfIntersection', this);
53346                     return;   // can't click here
53347                 }
53348
53349                 context.pauseChangeDispatch();
53350                 doAdd();
53351                 // we just replaced the temporary edit with the real one
53352                 _didResolveTempEdit = true;
53353                 context.resumeChangeDispatch();
53354
53355                 context.enter(mode);
53356             }
53357
53358
53359             // Accept the current position of the drawing node
53360             drawWay.add = function(loc, d) {
53361                 attemptAdd(d, loc, function() {
53362                     // don't need to do anything extra
53363                 });
53364             };
53365
53366
53367             // Connect the way to an existing way
53368             drawWay.addWay = function(loc, edge, d) {
53369                 attemptAdd(d, loc, function() {
53370                     context.replace(
53371                         actionAddMidpoint({ loc: loc, edge: edge }, _drawNode),
53372                         _annotation
53373                     );
53374                 });
53375             };
53376
53377
53378             // Connect the way to an existing node
53379             drawWay.addNode = function(node, d) {
53380
53381                 // finish drawing if the mapper targets the prior node
53382                 if (node.id === _headNodeID ||
53383                     // or the first node when drawing an area
53384                     (_origWay.isClosed() && node.id === _origWay.first())) {
53385                     drawWay.finish();
53386                     return;
53387                 }
53388
53389                 attemptAdd(d, node.loc, function() {
53390                     context.replace(
53391                         function actionReplaceDrawNode(graph) {
53392                             // remove the temporary draw node and insert the existing node
53393                             // at the same index
53394
53395                             graph = graph
53396                                 .replace(graph.entity(wayID).removeNode(_drawNode.id))
53397                                 .remove(_drawNode);
53398                             return graph
53399                                 .replace(graph.entity(wayID).addNode(node.id, _nodeIndex));
53400                         },
53401                         _annotation
53402                     );
53403                 });
53404             };
53405
53406
53407             // Finish the draw operation, removing the temporary edit.
53408             // If the way has enough nodes to be valid, it's selected.
53409             // Otherwise, delete everything and return to browse mode.
53410             drawWay.finish = function() {
53411                 checkGeometry(false /* includeDrawNode */);
53412                 if (context.surface().classed('nope')) {
53413                     dispatch$1.call('rejectedSelfIntersection', this);
53414                     return;   // can't click here
53415                 }
53416
53417                 context.pauseChangeDispatch();
53418                 // remove the temporary edit
53419                 context.pop(1);
53420                 _didResolveTempEdit = true;
53421                 context.resumeChangeDispatch();
53422
53423                 var way = context.hasEntity(wayID);
53424                 if (!way || way.isDegenerate()) {
53425                     drawWay.cancel();
53426                     return;
53427                 }
53428
53429                 window.setTimeout(function() {
53430                     context.map().dblclickZoomEnable(true);
53431                 }, 1000);
53432
53433                 var isNewFeature = !mode.isContinuing;
53434                 context.enter(modeSelect(context, [wayID]).newFeature(isNewFeature));
53435             };
53436
53437
53438             // Cancel the draw operation, delete everything, and return to browse mode.
53439             drawWay.cancel = function() {
53440                 context.pauseChangeDispatch();
53441                 resetToStartGraph();
53442                 context.resumeChangeDispatch();
53443
53444                 window.setTimeout(function() {
53445                     context.map().dblclickZoomEnable(true);
53446                 }, 1000);
53447
53448                 context.surface()
53449                     .classed('nope', false)
53450                     .classed('nope-disabled', false)
53451                     .classed('nope-suppressed', false);
53452
53453                 context.enter(modeBrowse(context));
53454             };
53455
53456
53457             drawWay.nodeIndex = function(val) {
53458                 if (!arguments.length) { return _nodeIndex; }
53459                 _nodeIndex = val;
53460                 return drawWay;
53461             };
53462
53463
53464             drawWay.activeID = function() {
53465                 if (!arguments.length) { return _drawNode && _drawNode.id; }
53466                 // no assign
53467                 return drawWay;
53468             };
53469
53470
53471             return utilRebind(drawWay, dispatch$1, 'on');
53472         }
53473
53474         function modeDrawLine(context, wayID, startGraph, button, affix, continuing) {
53475             var mode = {
53476                 button: button,
53477                 id: 'draw-line'
53478             };
53479
53480             var behavior = behaviorDrawWay(context, wayID, mode, startGraph)
53481                 .on('rejectedSelfIntersection.modeDrawLine', function() {
53482                     context.ui().flash
53483                         .text(_t('self_intersection.error.lines'))();
53484                 });
53485
53486             mode.wayID = wayID;
53487
53488             mode.isContinuing = continuing;
53489
53490             mode.enter = function() {
53491                 behavior
53492                     .nodeIndex(affix === 'prefix' ? 0 : undefined);
53493
53494                 context.install(behavior);
53495             };
53496
53497             mode.exit = function() {
53498                 context.uninstall(behavior);
53499             };
53500
53501             mode.selectedIDs = function() {
53502                 return [wayID];
53503             };
53504
53505             mode.activeID = function() {
53506                 return (behavior && behavior.activeID()) || [];
53507             };
53508
53509             return mode;
53510         }
53511
53512         function operationContinue(context, selectedIDs) {
53513             var graph = context.graph();
53514             var entities = selectedIDs.map(function(id) { return graph.entity(id); });
53515             var geometries = Object.assign(
53516                 { line: [], vertex: [] },
53517                 utilArrayGroupBy(entities, function(entity) { return entity.geometry(graph); })
53518             );
53519             var vertex = geometries.vertex[0];
53520
53521
53522             function candidateWays() {
53523                 return graph.parentWays(vertex).filter(function(parent) {
53524                     return parent.geometry(graph) === 'line' &&
53525                         !parent.isClosed() &&
53526                         parent.affix(vertex.id) &&
53527                         (geometries.line.length === 0 || geometries.line[0] === parent);
53528                 });
53529             }
53530
53531
53532             var operation = function() {
53533                 var candidate = candidateWays()[0];
53534                 context.enter(
53535                     modeDrawLine(context, candidate.id, context.graph(), 'line', candidate.affix(vertex.id), true)
53536                 );
53537             };
53538
53539
53540             operation.available = function() {
53541                 return geometries.vertex.length === 1 &&
53542                     geometries.line.length <= 1 &&
53543                     !context.features().hasHiddenConnections(vertex, context.graph());
53544             };
53545
53546
53547             operation.disabled = function() {
53548                 var candidates = candidateWays();
53549                 if (candidates.length === 0) {
53550                     return 'not_eligible';
53551                 } else if (candidates.length > 1) {
53552                     return 'multiple';
53553                 }
53554
53555                 return false;
53556             };
53557
53558
53559             operation.tooltip = function() {
53560                 var disable = operation.disabled();
53561                 return disable ?
53562                     _t('operations.continue.' + disable) :
53563                     _t('operations.continue.description');
53564             };
53565
53566
53567             operation.annotation = function() {
53568                 return _t('operations.continue.annotation.line');
53569             };
53570
53571
53572             operation.id = 'continue';
53573             operation.keys = [_t('operations.continue.key')];
53574             operation.title = _t('operations.continue.title');
53575             operation.behavior = behaviorOperation(context).which(operation);
53576
53577             return operation;
53578         }
53579
53580         function operationCopy(context, selectedIDs) {
53581
53582             var _multi = selectedIDs.length === 1 ? 'single' : 'multiple';
53583
53584             function getFilteredIdsToCopy() {
53585                 return selectedIDs.filter(function(selectedID) {
53586                     var entity = context.graph().hasEntity(selectedID);
53587                     // don't copy untagged vertices separately from ways
53588                     return entity.hasInterestingTags() || entity.geometry(context.graph()) !== 'vertex';
53589                 });
53590             }
53591
53592             var operation = function() {
53593
53594                 var graph = context.graph();
53595                 var selected = groupEntities(getFilteredIdsToCopy(), graph);
53596                 var canCopy = [];
53597                 var skip = {};
53598                 var entity;
53599                 var i;
53600
53601                 for (i = 0; i < selected.relation.length; i++) {
53602                     entity = selected.relation[i];
53603                     if (!skip[entity.id] && entity.isComplete(graph)) {
53604                         canCopy.push(entity.id);
53605                         skip = getDescendants(entity.id, graph, skip);
53606                     }
53607                 }
53608                 for (i = 0; i < selected.way.length; i++) {
53609                     entity = selected.way[i];
53610                     if (!skip[entity.id]) {
53611                         canCopy.push(entity.id);
53612                         skip = getDescendants(entity.id, graph, skip);
53613                     }
53614                 }
53615                 for (i = 0; i < selected.node.length; i++) {
53616                     entity = selected.node[i];
53617                     if (!skip[entity.id]) {
53618                         canCopy.push(entity.id);
53619                     }
53620                 }
53621
53622                 context.copyIDs(canCopy);
53623                 if (_point &&
53624                     (canCopy.length !== 1 || graph.entity(canCopy[0]).type !== 'node')) {
53625                     // store the anchor coordinates if copying more than a single node
53626                     context.copyLonLat(context.projection.invert(_point));
53627                 } else {
53628                     context.copyLonLat(null);
53629                 }
53630
53631             };
53632
53633
53634             function groupEntities(ids, graph) {
53635                 var entities = ids.map(function (id) { return graph.entity(id); });
53636                 return Object.assign(
53637                     { relation: [], way: [], node: [] },
53638                     utilArrayGroupBy(entities, 'type')
53639                 );
53640             }
53641
53642
53643             function getDescendants(id, graph, descendants) {
53644                 var entity = graph.entity(id);
53645                 var children;
53646
53647                 descendants = descendants || {};
53648
53649                 if (entity.type === 'relation') {
53650                     children = entity.members.map(function(m) { return m.id; });
53651                 } else if (entity.type === 'way') {
53652                     children = entity.nodes;
53653                 } else {
53654                     children = [];
53655                 }
53656
53657                 for (var i = 0; i < children.length; i++) {
53658                     if (!descendants[children[i]]) {
53659                         descendants[children[i]] = true;
53660                         descendants = getDescendants(children[i], graph, descendants);
53661                     }
53662                 }
53663
53664                 return descendants;
53665             }
53666
53667
53668             operation.available = function() {
53669                 return getFilteredIdsToCopy().length > 0;
53670             };
53671
53672
53673             operation.disabled = function() {
53674                 var extent = utilTotalExtent(getFilteredIdsToCopy(), context.graph());
53675                 if (extent.percentContainedIn(context.map().extent()) < 0.8) {
53676                     return 'too_large';
53677                 }
53678                 return false;
53679             };
53680
53681
53682             operation.availableForKeypress = function() {
53683                 var selection = window.getSelection && window.getSelection();
53684                 // if the user has text selected then let them copy that, not the selected feature
53685                 return !selection || !selection.toString();
53686             };
53687
53688
53689             operation.tooltip = function() {
53690                 var disable = operation.disabled();
53691                 return disable ?
53692                     _t('operations.copy.' + disable + '.' + _multi) :
53693                     _t('operations.copy.description' + '.' + _multi);
53694             };
53695
53696
53697             operation.annotation = function() {
53698                 return selectedIDs.length === 1 ?
53699                     _t('operations.copy.annotation.single') :
53700                     _t('operations.copy.annotation.multiple', { n: selectedIDs.length.toString() });
53701             };
53702
53703
53704             var _point;
53705             operation.point = function(val) {
53706                 _point = val;
53707                 return operation;
53708             };
53709
53710
53711             operation.id = 'copy';
53712             operation.keys = [uiCmd('⌘C')];
53713             operation.title = _t('operations.copy.title');
53714             operation.behavior = behaviorOperation(context).which(operation);
53715
53716             return operation;
53717         }
53718
53719         function operationDisconnect(context, selectedIDs) {
53720             var _vertexIDs = [];
53721             var _wayIDs = [];
53722             var _otherIDs = [];
53723             var _actions = [];
53724
53725             selectedIDs.forEach(function(id) {
53726                 var entity = context.entity(id);
53727                 if (entity.type === 'way'){
53728                     _wayIDs.push(id);
53729                 } else if (entity.geometry(context.graph()) === 'vertex') {
53730                     _vertexIDs.push(id);
53731                 } else {
53732                     _otherIDs.push(id);
53733                 }
53734             });
53735
53736             var _extent, _nodes, _coords, _descriptionID = '', _annotationID = 'features';
53737
53738             if (_vertexIDs.length > 0) {
53739                 // At the selected vertices, disconnect the selected ways, if any, else
53740                 // disconnect all connected ways
53741
53742                 _extent = utilTotalExtent(_vertexIDs, context.graph());
53743
53744                 _vertexIDs.forEach(function(vertexID) {
53745                     var action = actionDisconnect(vertexID);
53746
53747                     if (_wayIDs.length > 0) {
53748                         var waysIDsForVertex = _wayIDs.filter(function(wayID) {
53749                             var way = context.entity(wayID);
53750                             return way.nodes.indexOf(vertexID) !== -1;
53751                         });
53752                         action.limitWays(waysIDsForVertex);
53753                     }
53754                     _actions.push(action);
53755                 });
53756
53757                 _descriptionID += _actions.length === 1 ? 'single_point.' : 'multiple_points.';
53758                 if (_wayIDs.length === 1) {
53759                     _descriptionID += 'single_way.' + context.graph().geometry(_wayIDs[0]);
53760                 } else {
53761                     _descriptionID += _wayIDs.length === 0 ? 'no_ways' : 'multiple_ways';
53762                 }
53763
53764             } else if (_wayIDs.length > 0) {
53765                 // Disconnect the selected ways from each other, if they're connected,
53766                 // else disconnect them from all connected ways
53767
53768                 var ways = _wayIDs.map(function(id) {
53769                     return context.entity(id);
53770                 });
53771                 _nodes = utilGetAllNodes(_wayIDs, context.graph());
53772                 _coords = _nodes.map(function(n) { return n.loc; });
53773
53774                 // actions for connected nodes shared by at least two selected ways
53775                 var sharedActions = [];
53776                 var sharedNodes = [];
53777                 // actions for connected nodes
53778                 var unsharedActions = [];
53779                 var unsharedNodes = [];
53780
53781                 _nodes.forEach(function(node) {
53782                     var action = actionDisconnect(node.id).limitWays(_wayIDs);
53783                     if (action.disabled(context.graph()) !== 'not_connected') {
53784
53785                         var count = 0;
53786                         for (var i in ways) {
53787                             var way = ways[i];
53788                             if (way.nodes.indexOf(node.id) !== -1) {
53789                                 count += 1;
53790                             }
53791                             if (count > 1) { break; }
53792                         }
53793
53794                         if (count > 1) {
53795                             sharedActions.push(action);
53796                             sharedNodes.push(node);
53797                         } else {
53798                             unsharedActions.push(action);
53799                             unsharedNodes.push(node);
53800                         }
53801                     }
53802                 });
53803
53804                 _descriptionID += 'no_points.';
53805                 _descriptionID += _wayIDs.length === 1 ? 'single_way.' : 'multiple_ways.';
53806
53807                 if (sharedActions.length) {
53808                     // if any nodes are shared, only disconnect the selected ways from each other
53809                     _actions = sharedActions;
53810                     _extent = utilTotalExtent(sharedNodes, context.graph());
53811                     _descriptionID += 'conjoined';
53812                     _annotationID = 'from_each_other';
53813                 } else {
53814                     // if no nodes are shared, disconnect the selected ways from all connected ways
53815                     _actions = unsharedActions;
53816                     _extent = utilTotalExtent(unsharedNodes, context.graph());
53817                     if (_wayIDs.length === 1) {
53818                         _descriptionID += context.graph().geometry(_wayIDs[0]);
53819                     } else {
53820                         _descriptionID += 'separate';
53821                     }
53822                 }
53823             }
53824
53825
53826             var operation = function() {
53827                 context.perform(function(graph) {
53828                     return _actions.reduce(function(graph, action) { return action(graph); }, graph);
53829                 }, operation.annotation());
53830
53831                 context.validator().validate();
53832             };
53833
53834
53835             operation.available = function() {
53836                 if (_actions.length === 0) { return false; }
53837                 if (_otherIDs.length !== 0) { return false; }
53838
53839                 if (_vertexIDs.length !== 0 && _wayIDs.length !== 0 && !_wayIDs.every(function(wayID) {
53840                     return _vertexIDs.some(function(vertexID) {
53841                         var way = context.entity(wayID);
53842                         return way.nodes.indexOf(vertexID) !== -1;
53843                     });
53844                 })) { return false; }
53845
53846                 return true;
53847             };
53848
53849
53850             operation.disabled = function() {
53851                 var reason;
53852                 for (var actionIndex in _actions) {
53853                     reason = _actions[actionIndex].disabled(context.graph());
53854                     if (reason) { return reason; }
53855                 }
53856
53857                 if (_extent && _extent.percentContainedIn(context.map().extent()) < 0.8) {
53858                     return 'too_large.' + ((_vertexIDs.length ? _vertexIDs : _wayIDs).length === 1 ? 'single' : 'multiple');
53859                 } else if (_coords && someMissing()) {
53860                     return 'not_downloaded';
53861                 } else if (selectedIDs.some(context.hasHiddenConnections)) {
53862                     return 'connected_to_hidden';
53863                 }
53864
53865                 return false;
53866
53867
53868                 function someMissing() {
53869                     if (context.inIntro()) { return false; }
53870                     var osm = context.connection();
53871                     if (osm) {
53872                         var missing = _coords.filter(function(loc) { return !osm.isDataLoaded(loc); });
53873                         if (missing.length) {
53874                             missing.forEach(function(loc) { context.loadTileAtLoc(loc); });
53875                             return true;
53876                         }
53877                     }
53878                     return false;
53879                 }
53880             };
53881
53882
53883             operation.tooltip = function() {
53884                 var disable = operation.disabled();
53885                 if (disable) {
53886                     return _t('operations.disconnect.' + disable);
53887                 }
53888                 return _t('operations.disconnect.description.' + _descriptionID);
53889             };
53890
53891
53892             operation.annotation = function() {
53893                 return _t('operations.disconnect.annotation.' + _annotationID);
53894             };
53895
53896
53897             operation.id = 'disconnect';
53898             operation.keys = [_t('operations.disconnect.key')];
53899             operation.title = _t('operations.disconnect.title');
53900             operation.behavior = behaviorOperation(context).which(operation);
53901
53902             return operation;
53903         }
53904
53905         function operationDowngrade(context, selectedIDs) {
53906             var affectedFeatureCount = 0;
53907             var downgradeType;
53908
53909             setDowngradeTypeForEntityIDs();
53910
53911             var multi = affectedFeatureCount === 1 ? 'single' : 'multiple';
53912
53913             function setDowngradeTypeForEntityIDs() {
53914                 for (var i in selectedIDs) {
53915                     var entityID = selectedIDs[i];
53916                     var type = downgradeTypeForEntityID(entityID);
53917                     if (type) {
53918                         affectedFeatureCount += 1;
53919                         if (downgradeType && type !== downgradeType) {
53920                             downgradeType = 'building_address';
53921                         } else {
53922                             downgradeType = type;
53923                         }
53924                     }
53925                 }
53926             }
53927
53928             function downgradeTypeForEntityID(entityID) {
53929                 var graph = context.graph();
53930                 var entity = graph.entity(entityID);
53931                 var preset = _mainPresetIndex.match(entity, graph);
53932
53933                 if (!preset || preset.isFallback()) { return null; }
53934
53935                 if (entity.type === 'node' &&
53936                     preset.id !== 'address' &&
53937                     Object.keys(entity.tags).some(function(key) {
53938                         return key.match(/^addr:.{1,}/);
53939                     })) {
53940
53941                     return 'address';
53942                 }
53943                 if (entity.geometry(graph) === 'area' &&
53944                     entity.tags.building &&
53945                     !preset.tags.building) {
53946
53947                     return 'building';
53948                 }
53949
53950                 return null;
53951             }
53952
53953             var buildingKeysToKeep = ['architect', 'building', 'height', 'layer', 'source', 'type', 'wheelchair'];
53954             var addressKeysToKeep = ['source'];
53955
53956             var operation = function () {
53957                 context.perform(function(graph) {
53958
53959                     for (var i in selectedIDs) {
53960                         var entityID = selectedIDs[i];
53961                         var type = downgradeTypeForEntityID(entityID);
53962                         if (!type) { continue; }
53963
53964                         var tags = Object.assign({}, graph.entity(entityID).tags);  // shallow copy
53965                         for (var key in tags) {
53966                             if (type === 'address' && addressKeysToKeep.indexOf(key) !== -1) { continue; }
53967                             if (type === 'building') {
53968                                 if (buildingKeysToKeep.indexOf(key) !== -1 ||
53969                                     key.match(/^building:.{1,}/) ||
53970                                     key.match(/^roof:.{1,}/)) { continue; }
53971                             }
53972                             // keep address tags for buildings too
53973                             if (key.match(/^addr:.{1,}/)) { continue; }
53974
53975                             delete tags[key];
53976                         }
53977                         graph = actionChangeTags(entityID, tags)(graph);
53978                     }
53979                     return graph;
53980                 }, operation.annotation());
53981
53982                 context.validator().validate();
53983
53984                 // refresh the select mode to enable the delete operation
53985                 context.enter(modeSelect(context, selectedIDs));
53986             };
53987
53988
53989             operation.available = function () {
53990                 return downgradeType;
53991             };
53992
53993
53994             operation.disabled = function () {
53995                 if (selectedIDs.some(hasWikidataTag)) {
53996                     return 'has_wikidata_tag';
53997                 }
53998                 return false;
53999
54000                 function hasWikidataTag(id) {
54001                     var entity = context.entity(id);
54002                     return entity.tags.wikidata && entity.tags.wikidata.trim().length > 0;
54003                 }
54004             };
54005
54006
54007             operation.tooltip = function () {
54008                 var disable = operation.disabled();
54009                 return disable ?
54010                     _t('operations.downgrade.' + disable + '.' + multi) :
54011                     _t('operations.downgrade.description.' + downgradeType);
54012             };
54013
54014
54015             operation.annotation = function () {
54016                 var suffix;
54017                 if (downgradeType === 'building_address') {
54018                     suffix = 'multiple';
54019                 } else {
54020                     suffix = downgradeType + '.' + multi;
54021                 }
54022                 return _t('operations.downgrade.annotation.' + suffix, { n: affectedFeatureCount});
54023             };
54024
54025
54026             operation.id = 'downgrade';
54027             operation.keys = [uiCmd('⌘⌫'), uiCmd('⌘⌦'), uiCmd('⌦')];
54028             operation.title = _t('operations.downgrade.title');
54029             operation.behavior = behaviorOperation(context).which(operation);
54030
54031
54032             return operation;
54033         }
54034
54035         function operationExtract(context, selectedIDs) {
54036
54037             var _amount = selectedIDs.length === 1 ? 'single' : 'multiple';
54038             var _geometries = utilArrayUniq(selectedIDs.map(function(entityID) {
54039                 return context.graph().hasEntity(entityID) && context.graph().geometry(entityID);
54040             }).filter(Boolean));
54041             var _geometryID = _geometries.length === 1 ? _geometries[0] : 'feature';
54042
54043             var _extent;
54044             var _actions = selectedIDs.map(function(entityID) {
54045                 var graph = context.graph();
54046                 var entity = graph.hasEntity(entityID);
54047                 if (!entity || !entity.hasInterestingTags()) { return; }
54048
54049                 if (entity.type === 'node' && graph.parentWays(entity).length === 0) { return; }
54050
54051                 if (entity.type !== 'node') {
54052                     var preset = _mainPresetIndex.match(entity, graph);
54053                     // only allow extraction from ways/relations if the preset supports points
54054                     if (preset.geometry.indexOf('point') === -1) { return; }
54055                 }
54056
54057                 _extent = _extent ? _extent.extend(entity.extent(graph)) : entity.extent(graph);
54058
54059                 return actionExtract(entityID);
54060             }).filter(Boolean);
54061
54062
54063             var operation = function () {
54064                 var combinedAction = function(graph) {
54065                     _actions.forEach(function(action) {
54066                         graph = action(graph);
54067                     });
54068                     return graph;
54069                 };
54070                 context.perform(combinedAction, operation.annotation());  // do the extract
54071
54072                 var extractedNodeIDs = _actions.map(function(action) {
54073                     return action.getExtractedNodeID();
54074                 });
54075                 context.enter(modeSelect(context, extractedNodeIDs));
54076             };
54077
54078
54079             operation.available = function () {
54080                 return _actions.length && selectedIDs.length === _actions.length;
54081             };
54082
54083
54084             operation.disabled = function () {
54085
54086                 if (_extent && _extent.percentContainedIn(context.map().extent()) < 0.8) {
54087                     return 'too_large';
54088                 } else if (selectedIDs.some(function(entityID) {
54089                     return context.graph().geometry(entityID) === 'vertex' && context.hasHiddenConnections(entityID);
54090                 })) {
54091                     return 'connected_to_hidden';
54092                 }
54093
54094                 return false;
54095             };
54096
54097
54098             operation.tooltip = function () {
54099                 var disableReason = operation.disabled();
54100                 if (disableReason) {
54101                     return _t('operations.extract.' + disableReason + '.' + _amount);
54102                 } else {
54103                     return _t('operations.extract.description.' + _geometryID + '.' + _amount);
54104                 }
54105             };
54106
54107
54108             operation.annotation = function () {
54109                 return _t('operations.extract.annotation.' + _amount, { n: selectedIDs.length });
54110             };
54111
54112
54113             operation.id = 'extract';
54114             operation.keys = [_t('operations.extract.key')];
54115             operation.title = _t('operations.extract.title');
54116             operation.behavior = behaviorOperation(context).which(operation);
54117
54118
54119             return operation;
54120         }
54121
54122         function operationMerge(context, selectedIDs) {
54123
54124             var _action = getAction();
54125
54126             function getAction() {
54127                 // prefer a non-disabled action first
54128                 var join = actionJoin(selectedIDs);
54129                 if (!join.disabled(context.graph())) { return join; }
54130
54131                 var merge = actionMerge(selectedIDs);
54132                 if (!merge.disabled(context.graph())) { return merge; }
54133
54134                 var mergePolygon = actionMergePolygon(selectedIDs);
54135                 if (!mergePolygon.disabled(context.graph())) { return mergePolygon; }
54136
54137                 var mergeNodes = actionMergeNodes(selectedIDs);
54138                 if (!mergeNodes.disabled(context.graph())) { return mergeNodes; }
54139
54140                 // otherwise prefer an action with an interesting disabled reason
54141                 if (join.disabled(context.graph()) !== 'not_eligible') { return join; }
54142                 if (merge.disabled(context.graph()) !== 'not_eligible') { return merge; }
54143                 if (mergePolygon.disabled(context.graph()) !== 'not_eligible') { return mergePolygon; }
54144
54145                 return mergeNodes;
54146             }
54147
54148             var operation = function() {
54149
54150                 if (operation.disabled()) { return; }
54151
54152                 context.perform(_action, operation.annotation());
54153
54154                 context.validator().validate();
54155
54156                 var resultIDs = selectedIDs.filter(context.hasEntity);
54157                 if (resultIDs.length > 1) {
54158                     var interestingIDs = resultIDs.filter(function(id) {
54159                         return context.entity(id).hasInterestingTags();
54160                     });
54161                     if (interestingIDs.length) { resultIDs = interestingIDs; }
54162                 }
54163                 context.enter(modeSelect(context, resultIDs));
54164             };
54165
54166             operation.available = function() {
54167                 return selectedIDs.length >= 2;
54168             };
54169
54170             operation.disabled = function() {
54171                 var actionDisabled = _action.disabled(context.graph());
54172                 if (actionDisabled) { return actionDisabled; }
54173
54174                 var osm = context.connection();
54175                 if (osm &&
54176                     _action.resultingWayNodesLength &&
54177                     _action.resultingWayNodesLength(context.graph()) > osm.maxWayNodes()) {
54178                     return 'too_many_vertices';
54179                 }
54180
54181                 return false;
54182             };
54183
54184             operation.tooltip = function() {
54185                 var disabled = operation.disabled();
54186                 if (disabled) {
54187                     if (disabled === 'restriction') {
54188                         return _t('operations.merge.restriction',
54189                             { relation: _mainPresetIndex.item('type/restriction').name() });
54190                     }
54191                     return _t('operations.merge.' + disabled);
54192                 }
54193                 return _t('operations.merge.description');
54194             };
54195
54196             operation.annotation = function() {
54197                 return _t('operations.merge.annotation', { n: selectedIDs.length });
54198             };
54199
54200             operation.id = 'merge';
54201             operation.keys = [_t('operations.merge.key')];
54202             operation.title = _t('operations.merge.title');
54203             operation.behavior = behaviorOperation(context).which(operation);
54204
54205             return operation;
54206         }
54207
54208         // see also `behaviorPaste`
54209         function operationPaste(context) {
54210
54211             var _pastePoint;
54212
54213             var operation = function() {
54214
54215                 if (!_pastePoint) { return; }
54216
54217                 var oldIDs = context.copyIDs();
54218                 if (!oldIDs.length) { return; }
54219
54220                 var projection = context.projection;
54221                 var extent = geoExtent();
54222                 var oldGraph = context.copyGraph();
54223                 var newIDs = [];
54224
54225                 var action = actionCopyEntities(oldIDs, oldGraph);
54226                 context.perform(action);
54227
54228                 var copies = action.copies();
54229                 var originals = new Set();
54230                 Object.values(copies).forEach(function(entity) { originals.add(entity.id); });
54231
54232                 for (var id in copies) {
54233                     var oldEntity = oldGraph.entity(id);
54234                     var newEntity = copies[id];
54235
54236                     extent._extend(oldEntity.extent(oldGraph));
54237
54238                     // Exclude child nodes from newIDs if their parent way was also copied.
54239                     var parents = context.graph().parentWays(newEntity);
54240                     var parentCopied = parents.some(function(parent) {
54241                         return originals.has(parent.id);
54242                     });
54243
54244                     if (!parentCopied) {
54245                         newIDs.push(newEntity.id);
54246                     }
54247                 }
54248
54249                 // Use the location of the copy operation to offset the paste location,
54250                 // or else use the center of the pasted extent
54251                 var copyPoint = (context.copyLonLat() && projection(context.copyLonLat())) ||
54252                     projection(extent.center());
54253                 var delta = geoVecSubtract(_pastePoint, copyPoint);
54254
54255                 // Move the pasted objects to be anchored at the paste location
54256                 context.replace(actionMove(newIDs, delta, projection), operation.annotation());
54257                 context.enter(modeSelect(context, newIDs));
54258             };
54259
54260             operation.point = function(val) {
54261                 _pastePoint = val;
54262                 return operation;
54263             };
54264
54265             operation.available = function() {
54266                 return context.mode().id === 'browse';
54267             };
54268
54269             operation.disabled = function() {
54270                 return !context.copyIDs().length;
54271             };
54272
54273             operation.tooltip = function() {
54274                 var oldGraph = context.copyGraph();
54275                 var ids = context.copyIDs();
54276                 if (!ids.length) {
54277                     return _t('operations.paste.nothing_copied');
54278                 }
54279                 return ids.length === 1 ?
54280                     _t('operations.paste.description.single', { feature: utilDisplayLabel(oldGraph.entity(ids[0]), oldGraph) }) :
54281                     _t('operations.paste.description.multiple', { n: ids.length.toString() });
54282             };
54283
54284             operation.annotation = function() {
54285                 var ids = context.copyIDs();
54286                 return ids.length === 1 ?
54287                     _t('operations.paste.annotation.single') :
54288                     _t('operations.paste.annotation.multiple', { n: ids.length.toString() });
54289             };
54290
54291             operation.id = 'paste';
54292             operation.keys = [uiCmd('⌘V')];
54293             operation.title = _t('operations.paste.title');
54294
54295             return operation;
54296         }
54297
54298         function operationReverse(context, selectedIDs) {
54299
54300             var operation = function() {
54301                 context.perform(function combinedReverseAction(graph) {
54302                     actions().forEach(function(action) {
54303                         graph = action(graph);
54304                     });
54305                     return graph;
54306                 }, operation.annotation());
54307                 context.validator().validate();
54308             };
54309
54310             function actions(situation) {
54311                 return selectedIDs.map(function(entityID) {
54312                     var entity = context.hasEntity(entityID);
54313                     if (!entity) { return; }
54314
54315                     if (situation === 'toolbar') {
54316                         if (entity.type === 'way' &&
54317                             (!entity.isOneWay() && !entity.isSided())) { return; }
54318                     }
54319
54320                     var geometry = entity.geometry(context.graph());
54321                     if (entity.type !== 'node' && geometry !== 'line') { return; }
54322
54323                     var action = actionReverse(entityID);
54324                     if (action.disabled(context.graph())) { return; }
54325
54326                     return action;
54327                 }).filter(Boolean);
54328             }
54329
54330             function reverseTypeID() {
54331                 var acts = actions();
54332                 var nodeActionCount = acts.filter(function(act) {
54333                     var entity = context.hasEntity(act.entityID());
54334                     return entity && entity.type === 'node';
54335                 }).length;
54336                 var typeID = nodeActionCount === 0 ? 'line' : (nodeActionCount === acts.length ? 'point' : 'features');
54337                 if (typeID !== 'features' && acts.length > 1) { typeID += 's'; }
54338                 return typeID;
54339             }
54340
54341
54342             operation.available = function(situation) {
54343                 return actions(situation).length > 0;
54344             };
54345
54346
54347             operation.disabled = function() {
54348                 return false;
54349             };
54350
54351
54352             operation.tooltip = function() {
54353                 return _t('operations.reverse.description.' + reverseTypeID());
54354             };
54355
54356
54357             operation.annotation = function() {
54358                 return _t('operations.reverse.annotation.' + reverseTypeID());
54359             };
54360
54361
54362             operation.id = 'reverse';
54363             operation.keys = [_t('operations.reverse.key')];
54364             operation.title = _t('operations.reverse.title');
54365             operation.behavior = behaviorOperation(context).which(operation);
54366
54367             return operation;
54368         }
54369
54370         function operationSplit(context, selectedIDs) {
54371             var vertices = selectedIDs
54372                 .filter(function(id) { return context.graph().geometry(id) === 'vertex'; });
54373             var entityID = vertices[0];
54374             var action = actionSplit(entityID);
54375             var ways = [];
54376
54377             if (vertices.length === 1) {
54378                 if (entityID && selectedIDs.length > 1) {
54379                     var ids = selectedIDs.filter(function(id) { return id !== entityID; });
54380                     action.limitWays(ids);
54381                 }
54382                 ways = action.ways(context.graph());
54383             }
54384
54385
54386             var operation = function() {
54387                 var difference = context.perform(action, operation.annotation());
54388                 context.enter(modeSelect(context, difference.extantIDs()));
54389             };
54390
54391
54392             operation.available = function() {
54393                 return vertices.length === 1;
54394             };
54395
54396
54397             operation.disabled = function() {
54398                 var reason = action.disabled(context.graph());
54399                 if (reason) {
54400                     return reason;
54401                 } else if (selectedIDs.some(context.hasHiddenConnections)) {
54402                     return 'connected_to_hidden';
54403                 }
54404
54405                 return false;
54406             };
54407
54408
54409             operation.tooltip = function() {
54410                 var disable = operation.disabled();
54411                 if (disable) {
54412                     return _t('operations.split.' + disable);
54413                 } else if (ways.length === 1) {
54414                     return _t('operations.split.description.' + context.graph().geometry(ways[0].id));
54415                 } else {
54416                     return _t('operations.split.description.multiple');
54417                 }
54418             };
54419
54420
54421             operation.annotation = function() {
54422                 return ways.length === 1 ?
54423                     _t('operations.split.annotation.' + context.graph().geometry(ways[0].id)) :
54424                     _t('operations.split.annotation.multiple', { n: ways.length });
54425             };
54426
54427
54428             operation.id = 'split';
54429             operation.keys = [_t('operations.split.key')];
54430             operation.title = _t('operations.split.title');
54431             operation.behavior = behaviorOperation(context).which(operation);
54432
54433             return operation;
54434         }
54435
54436         function operationStraighten(context, selectedIDs) {
54437             var _wayIDs = selectedIDs.filter(function(id) { return id.charAt(0) === 'w'; });
54438             var _nodeIDs = selectedIDs.filter(function(id) { return id.charAt(0) === 'n'; });
54439             var _amount = ((_wayIDs.length ? _wayIDs : _nodeIDs).length === 1 ? 'single' : 'multiple');
54440
54441             var _nodes = utilGetAllNodes(selectedIDs, context.graph());
54442             var _coords = _nodes.map(function(n) { return n.loc; });
54443             var _extent = utilTotalExtent(selectedIDs, context.graph());
54444             var _action = chooseAction();
54445             var _geometry;
54446
54447
54448             function chooseAction() {
54449                 // straighten selected nodes
54450                 if (_wayIDs.length === 0 && _nodeIDs.length > 2) {
54451                     _geometry = 'points';
54452                     return actionStraightenNodes(_nodeIDs, context.projection);
54453
54454                 // straighten selected ways (possibly between range of 2 selected nodes)
54455                 } else if (_wayIDs.length > 0 && (_nodeIDs.length === 0 || _nodeIDs.length === 2)) {
54456                     var startNodeIDs = [];
54457                     var endNodeIDs = [];
54458
54459                     for (var i = 0; i < selectedIDs.length; i++) {
54460                         var entity = context.entity(selectedIDs[i]);
54461                         if (entity.type === 'node') {
54462                             continue;
54463                         } else if (entity.type !== 'way' || entity.isClosed()) {
54464                             return null;  // exit early, can't straighten these
54465                         }
54466
54467                         startNodeIDs.push(entity.first());
54468                         endNodeIDs.push(entity.last());
54469                     }
54470
54471                     // Remove duplicate end/startNodeIDs (duplicate nodes cannot be at the line end)
54472                     startNodeIDs = startNodeIDs.filter(function(n) {
54473                         return startNodeIDs.indexOf(n) === startNodeIDs.lastIndexOf(n);
54474                     });
54475                     endNodeIDs = endNodeIDs.filter(function(n) {
54476                         return endNodeIDs.indexOf(n) === endNodeIDs.lastIndexOf(n);
54477                     });
54478
54479                     // Ensure all ways are connected (i.e. only 2 unique endpoints/startpoints)
54480                     if (utilArrayDifference(startNodeIDs, endNodeIDs).length +
54481                         utilArrayDifference(endNodeIDs, startNodeIDs).length !== 2) { return null; }
54482
54483                     // Ensure path contains at least 3 unique nodes
54484                     var wayNodeIDs = utilGetAllNodes(_wayIDs, context.graph())
54485                         .map(function(node) { return node.id; });
54486                     if (wayNodeIDs.length <= 2) { return null; }
54487
54488                     // If range of 2 selected nodes is supplied, ensure nodes lie on the selected path
54489                     if (_nodeIDs.length === 2 && (
54490                         wayNodeIDs.indexOf(_nodeIDs[0]) === -1 || wayNodeIDs.indexOf(_nodeIDs[1]) === -1
54491                     )) { return null; }
54492
54493                     if (_nodeIDs.length) {
54494                         // If we're only straightenting between two points, we only need that extent visible
54495                         _extent = utilTotalExtent(_nodeIDs, context.graph());
54496                     }
54497
54498                     _geometry = _wayIDs.length === 1 ? 'line' : 'lines';
54499                     return actionStraightenWay(selectedIDs, context.projection);
54500                 }
54501
54502                 return null;
54503             }
54504
54505
54506             function operation() {
54507                 if (!_action) { return; }
54508
54509                 context.perform(_action, operation.annotation());
54510
54511                 window.setTimeout(function() {
54512                     context.validator().validate();
54513                 }, 300);  // after any transition
54514             }
54515
54516
54517             operation.available = function() {
54518                 return Boolean(_action);
54519             };
54520
54521
54522             operation.disabled = function() {
54523                 var reason = _action.disabled(context.graph());
54524                 if (reason) {
54525                     return reason;
54526                 } else if (_extent.percentContainedIn(context.map().extent()) < 0.8) {
54527                     return 'too_large';
54528                 } else if (someMissing()) {
54529                     return 'not_downloaded';
54530                 } else if (selectedIDs.some(context.hasHiddenConnections)) {
54531                     return 'connected_to_hidden';
54532                 }
54533
54534                 return false;
54535
54536
54537                 function someMissing() {
54538                     if (context.inIntro()) { return false; }
54539                     var osm = context.connection();
54540                     if (osm) {
54541                         var missing = _coords.filter(function(loc) { return !osm.isDataLoaded(loc); });
54542                         if (missing.length) {
54543                             missing.forEach(function(loc) { context.loadTileAtLoc(loc); });
54544                             return true;
54545                         }
54546                     }
54547                     return false;
54548                 }
54549             };
54550
54551
54552             operation.tooltip = function() {
54553                 var disable = operation.disabled();
54554                 return disable ?
54555                     _t('operations.straighten.' + disable + '.' + _amount) :
54556                     _t('operations.straighten.description.' + _geometry);
54557             };
54558
54559
54560             operation.annotation = function() {
54561                 return _t('operations.straighten.annotation.' + _geometry);
54562             };
54563
54564
54565             operation.id = 'straighten';
54566             operation.keys = [_t('operations.straighten.key')];
54567             operation.title = _t('operations.straighten.title');
54568             operation.behavior = behaviorOperation(context).which(operation);
54569
54570             return operation;
54571         }
54572
54573         var Operations = /*#__PURE__*/Object.freeze({
54574                 __proto__: null,
54575                 operationCircularize: operationCircularize,
54576                 operationContinue: operationContinue,
54577                 operationCopy: operationCopy,
54578                 operationDelete: operationDelete,
54579                 operationDisconnect: operationDisconnect,
54580                 operationDowngrade: operationDowngrade,
54581                 operationExtract: operationExtract,
54582                 operationMerge: operationMerge,
54583                 operationMove: operationMove,
54584                 operationOrthogonalize: operationOrthogonalize,
54585                 operationPaste: operationPaste,
54586                 operationReflectShort: operationReflectShort,
54587                 operationReflectLong: operationReflectLong,
54588                 operationReverse: operationReverse,
54589                 operationRotate: operationRotate,
54590                 operationSplit: operationSplit,
54591                 operationStraighten: operationStraighten
54592         });
54593
54594         var _relatedParent;
54595
54596
54597         function modeSelect(context, selectedIDs) {
54598             var mode = {
54599                 id: 'select',
54600                 button: 'browse'
54601             };
54602
54603             var keybinding = utilKeybinding('select');
54604
54605             var _breatheBehavior = behaviorBreathe();
54606             var _modeDragNode = modeDragNode(context);
54607             var _selectBehavior;
54608             var _behaviors = [];
54609
54610             var _operations = [];
54611             var _newFeature = false;
54612             var _follow = false;
54613
54614
54615             function singular() {
54616                 if (selectedIDs && selectedIDs.length === 1) {
54617                     return context.hasEntity(selectedIDs[0]);
54618                 }
54619             }
54620
54621             function selectedEntities() {
54622                 return selectedIDs.map(function(id) {
54623                     return context.hasEntity(id);
54624                 }).filter(Boolean);
54625             }
54626
54627
54628             function checkSelectedIDs() {
54629                 var ids = [];
54630                 if (Array.isArray(selectedIDs)) {
54631                     ids = selectedIDs.filter(function(id) {
54632                         return context.hasEntity(id);
54633                     });
54634                 }
54635
54636                 if (!ids.length) {
54637                     context.enter(modeBrowse(context));
54638                     return false;
54639                 } else if ((selectedIDs.length > 1 && ids.length === 1) ||
54640                     (selectedIDs.length === 1 && ids.length > 1)) {
54641                     // switch between single- and multi-select UI
54642                     context.enter(modeSelect(context, ids));
54643                     return false;
54644                 }
54645
54646                 selectedIDs = ids;
54647                 return true;
54648             }
54649
54650
54651             // find the common parent ways for nextVertex, previousVertex
54652             function commonParents() {
54653                 var graph = context.graph();
54654                 var commonParents = [];
54655
54656                 for (var i = 0; i < selectedIDs.length; i++) {
54657                     var entity = context.hasEntity(selectedIDs[i]);
54658                     if (!entity || entity.geometry(graph) !== 'vertex') {
54659                         return [];  // selection includes some not vertices
54660                     }
54661
54662                     var currParents = graph.parentWays(entity).map(function(w) { return w.id; });
54663                     if (!commonParents.length) {
54664                         commonParents = currParents;
54665                         continue;
54666                     }
54667
54668                     commonParents = utilArrayIntersection(commonParents, currParents);
54669                     if (!commonParents.length) {
54670                         return [];
54671                     }
54672                 }
54673
54674                 return commonParents;
54675             }
54676
54677
54678             function singularParent() {
54679                 var parents = commonParents();
54680                 if (!parents || parents.length === 0) {
54681                     _relatedParent = null;
54682                     return null;
54683                 }
54684
54685                 // relatedParent is used when we visit a vertex with multiple
54686                 // parents, and we want to remember which parent line we started on.
54687
54688                 if (parents.length === 1) {
54689                     _relatedParent = parents[0];  // remember this parent for later
54690                     return _relatedParent;
54691                 }
54692
54693                 if (parents.indexOf(_relatedParent) !== -1) {
54694                     return _relatedParent;   // prefer the previously seen parent
54695                 }
54696
54697                 return parents[0];
54698             }
54699
54700
54701             mode.selectedIDs = function(val) {
54702                 if (!arguments.length) { return selectedIDs; }
54703                 selectedIDs = val;
54704                 return mode;
54705             };
54706
54707
54708             mode.zoomToSelected = function() {
54709                 context.map().zoomToEase(selectedEntities());
54710             };
54711
54712
54713             mode.newFeature = function(val) {
54714                 if (!arguments.length) { return _newFeature; }
54715                 _newFeature = val;
54716                 return mode;
54717             };
54718
54719
54720             mode.selectBehavior = function(val) {
54721                 if (!arguments.length) { return _selectBehavior; }
54722                 _selectBehavior = val;
54723                 return mode;
54724             };
54725
54726
54727             mode.follow = function(val) {
54728                 if (!arguments.length) { return _follow; }
54729                 _follow = val;
54730                 return mode;
54731             };
54732
54733             function loadOperations() {
54734
54735                 _operations.forEach(function(operation) {
54736                     if (operation.behavior) {
54737                         context.uninstall(operation.behavior);
54738                     }
54739                 });
54740
54741                 _operations = Object.values(Operations)
54742                     .map(function(o) { return o(context, selectedIDs); })
54743                     .filter(function(o) { return o.available() && o.id !== 'delete' && o.id !== 'downgrade' && o.id !== 'copy'; });
54744
54745                 var copyOperation = operationCopy(context, selectedIDs);
54746                 if (copyOperation.available()) {
54747                     // group copy operation with delete/downgrade
54748                     _operations.push(copyOperation);
54749                 }
54750
54751                 var downgradeOperation = operationDowngrade(context, selectedIDs);
54752                 // don't allow delete if downgrade is available
54753                 var lastOperation = !context.inIntro() && downgradeOperation.available() ? downgradeOperation : operationDelete(context, selectedIDs);
54754
54755                 _operations.push(lastOperation);
54756
54757                 _operations.forEach(function(operation) {
54758                     if (operation.behavior) {
54759                         context.install(operation.behavior);
54760                     }
54761                 });
54762
54763                 // remove any displayed menu
54764                 context.ui().closeEditMenu();
54765             }
54766
54767             mode.operations = function() {
54768                 return _operations;
54769             };
54770
54771
54772             mode.enter = function() {
54773                 if (!checkSelectedIDs()) { return; }
54774
54775                 context.features().forceVisible(selectedIDs);
54776
54777                 _modeDragNode.restoreSelectedIDs(selectedIDs);
54778
54779                 loadOperations();
54780
54781                 if (!_behaviors.length) {
54782                     if (!_selectBehavior) { _selectBehavior = behaviorSelect(context); }
54783
54784                     _behaviors = [
54785                         behaviorPaste(context),
54786                         _breatheBehavior,
54787                         behaviorHover(context).on('hover', context.ui().sidebar.hoverModeSelect),
54788                         _selectBehavior,
54789                         behaviorLasso(context),
54790                         _modeDragNode.behavior,
54791                         modeDragNote(context).behavior
54792                     ];
54793                 }
54794                 _behaviors.forEach(context.install);
54795
54796                 keybinding
54797                     .on(_t('inspector.zoom_to.key'), mode.zoomToSelected)
54798                     .on(['[', 'pgup'], previousVertex)
54799                     .on([']', 'pgdown'], nextVertex)
54800                     .on(['{', uiCmd('⌘['), 'home'], firstVertex)
54801                     .on(['}', uiCmd('⌘]'), 'end'], lastVertex)
54802                     .on(uiCmd('⇧←'), nudgeSelection([-10, 0]))
54803                     .on(uiCmd('⇧↑'), nudgeSelection([0, -10]))
54804                     .on(uiCmd('⇧→'), nudgeSelection([10, 0]))
54805                     .on(uiCmd('⇧↓'), nudgeSelection([0, 10]))
54806                     .on(uiCmd('⇧⌘←'), nudgeSelection([-100, 0]))
54807                     .on(uiCmd('⇧⌘↑'), nudgeSelection([0, -100]))
54808                     .on(uiCmd('⇧⌘→'), nudgeSelection([100, 0]))
54809                     .on(uiCmd('⇧⌘↓'), nudgeSelection([0, 100]))
54810                     .on(['\\', 'pause'], nextParent)
54811                     .on('⎋', esc, true);
54812
54813                 select(document)
54814                     .call(keybinding);
54815
54816                 context.ui().sidebar
54817                     .select(selectedIDs, _newFeature);
54818
54819                 context.history()
54820                     .on('change.select', function() {
54821                         loadOperations();
54822                         // reselect after change in case relation members were removed or added
54823                         selectElements();
54824                     })
54825                     .on('undone.select', checkSelectedIDs)
54826                     .on('redone.select', checkSelectedIDs);
54827
54828                 context.map()
54829                     .on('drawn.select', selectElements)
54830                     .on('crossEditableZoom.select', function() {
54831                         selectElements();
54832                         _breatheBehavior.restartIfNeeded(context.surface());
54833                     });
54834
54835                 context.map().doubleUpHandler()
54836                     .on('doubleUp.modeSelect', didDoubleUp);
54837
54838
54839                 selectElements();
54840
54841                 if (_follow) {
54842                     var extent = geoExtent();
54843                     var graph = context.graph();
54844                     selectedIDs.forEach(function(id) {
54845                         var entity = context.entity(id);
54846                         extent._extend(entity.extent(graph));
54847                     });
54848
54849                     var loc = extent.center();
54850                     context.map().centerEase(loc);
54851                     // we could enter the mode multiple times, so reset follow for next time
54852                     _follow = false;
54853                 }
54854
54855
54856                 function nudgeSelection(delta) {
54857                     return function() {
54858                         // prevent nudging during low zoom selection
54859                         if (!context.map().withinEditableZoom()) { return; }
54860
54861                         var moveOp = operationMove(context, selectedIDs);
54862                         if (moveOp.disabled()) {
54863                             context.ui().flash
54864                                 .duration(4000)
54865                                 .iconName('#iD-operation-' + moveOp.id)
54866                                 .iconClass('operation disabled')
54867                                 .text(moveOp.tooltip)();
54868                         } else {
54869                             context.perform(actionMove(selectedIDs, delta, context.projection), moveOp.annotation());
54870                         }
54871                     };
54872                 }
54873
54874
54875                 function didDoubleUp(loc) {
54876                     if (!context.map().withinEditableZoom()) { return; }
54877
54878                     var target = select(event.target);
54879
54880                     var datum = target.datum();
54881                     var entity = datum && datum.properties && datum.properties.entity;
54882                     if (!entity) { return; }
54883
54884                     if (entity instanceof osmWay && target.classed('target')) {
54885                         var choice = geoChooseEdge(context.graph().childNodes(entity), loc, context.projection);
54886                         var prev = entity.nodes[choice.index - 1];
54887                         var next = entity.nodes[choice.index];
54888
54889                         context.perform(
54890                             actionAddMidpoint({ loc: choice.loc, edge: [prev, next] }, osmNode()),
54891                             _t('operations.add.annotation.vertex')
54892                         );
54893
54894                     } else if (entity.type === 'midpoint') {
54895                         context.perform(
54896                             actionAddMidpoint({ loc: entity.loc, edge: entity.edge }, osmNode()),
54897                             _t('operations.add.annotation.vertex'));
54898                     }
54899                 }
54900
54901
54902                 function selectElements() {
54903                     if (!checkSelectedIDs()) { return; }
54904
54905                     var surface = context.surface();
54906
54907                     surface.selectAll('.selected-member')
54908                         .classed('selected-member', false);
54909
54910                     surface.selectAll('.selected')
54911                         .classed('selected', false);
54912
54913                     surface.selectAll('.related')
54914                         .classed('related', false);
54915
54916                     singularParent();
54917                     if (_relatedParent) {
54918                         surface.selectAll(utilEntitySelector([_relatedParent]))
54919                             .classed('related', true);
54920                     }
54921
54922                     if (context.map().withinEditableZoom()) {
54923                         // Apply selection styling if not in wide selection
54924
54925                         surface
54926                             .selectAll(utilDeepMemberSelector(selectedIDs, context.graph(), true /* skipMultipolgonMembers */))
54927                             .classed('selected-member', true);
54928                         surface
54929                             .selectAll(utilEntityOrDeepMemberSelector(selectedIDs, context.graph()))
54930                             .classed('selected', true);
54931                     }
54932
54933                 }
54934
54935
54936                 function esc() {
54937                     if (context.container().select('.combobox').size()) { return; }
54938                     context.enter(modeBrowse(context));
54939                 }
54940
54941
54942                 function firstVertex() {
54943                     event.preventDefault();
54944                     var entity = singular();
54945                     var parent = singularParent();
54946                     var way;
54947
54948                     if (entity && entity.type === 'way') {
54949                         way = entity;
54950                     } else if (parent) {
54951                         way = context.entity(parent);
54952                     }
54953
54954                     if (way) {
54955                         context.enter(
54956                             modeSelect(context, [way.first()]).follow(true)
54957                         );
54958                     }
54959                 }
54960
54961
54962                 function lastVertex() {
54963                     event.preventDefault();
54964                     var entity = singular();
54965                     var parent = singularParent();
54966                     var way;
54967
54968                     if (entity && entity.type === 'way') {
54969                         way = entity;
54970                     } else if (parent) {
54971                         way = context.entity(parent);
54972                     }
54973
54974                     if (way) {
54975                         context.enter(
54976                             modeSelect(context, [way.last()]).follow(true)
54977                         );
54978                     }
54979                 }
54980
54981
54982                 function previousVertex() {
54983                     event.preventDefault();
54984                     var parent = singularParent();
54985                     if (!parent) { return; }
54986
54987                     var way = context.entity(parent);
54988                     var length = way.nodes.length;
54989                     var curr = way.nodes.indexOf(selectedIDs[0]);
54990                     var index = -1;
54991
54992                     if (curr > 0) {
54993                         index = curr - 1;
54994                     } else if (way.isClosed()) {
54995                         index = length - 2;
54996                     }
54997
54998                     if (index !== -1) {
54999                         context.enter(
55000                             modeSelect(context, [way.nodes[index]]).follow(true)
55001                         );
55002                     }
55003                 }
55004
55005
55006                 function nextVertex() {
55007                     event.preventDefault();
55008                     var parent = singularParent();
55009                     if (!parent) { return; }
55010
55011                     var way = context.entity(parent);
55012                     var length = way.nodes.length;
55013                     var curr = way.nodes.indexOf(selectedIDs[0]);
55014                     var index = -1;
55015
55016                     if (curr < length - 1) {
55017                         index = curr + 1;
55018                     } else if (way.isClosed()) {
55019                         index = 0;
55020                     }
55021
55022                     if (index !== -1) {
55023                         context.enter(
55024                             modeSelect(context, [way.nodes[index]]).follow(true)
55025                         );
55026                     }
55027                 }
55028
55029
55030                 function nextParent() {
55031                     event.preventDefault();
55032                     var parents = commonParents();
55033                     if (!parents || parents.length < 2) { return; }
55034
55035                     var index = parents.indexOf(_relatedParent);
55036                     if (index < 0 || index > parents.length - 2) {
55037                         _relatedParent = parents[0];
55038                     } else {
55039                         _relatedParent = parents[index + 1];
55040                     }
55041
55042                     var surface = context.surface();
55043                     surface.selectAll('.related')
55044                         .classed('related', false);
55045
55046                     if (_relatedParent) {
55047                         surface.selectAll(utilEntitySelector([_relatedParent]))
55048                             .classed('related', true);
55049                     }
55050                 }
55051             };
55052
55053
55054             mode.exit = function() {
55055
55056                 _newFeature = false;
55057
55058                 _operations.forEach(function(operation) {
55059                     if (operation.behavior) {
55060                         context.uninstall(operation.behavior);
55061                     }
55062                 });
55063                 _operations = [];
55064
55065                 _behaviors.forEach(context.uninstall);
55066
55067                 select(document)
55068                     .call(keybinding.unbind);
55069
55070                 context.ui().closeEditMenu();
55071
55072                 context.history()
55073                     .on('change.select', null)
55074                     .on('undone.select', null)
55075                     .on('redone.select', null);
55076
55077                 var surface = context.surface();
55078
55079                 surface
55080                     .selectAll('.selected-member')
55081                     .classed('selected-member', false);
55082
55083                 surface
55084                     .selectAll('.selected')
55085                     .classed('selected', false);
55086
55087                 surface
55088                     .selectAll('.highlighted')
55089                     .classed('highlighted', false);
55090
55091                 surface
55092                     .selectAll('.related')
55093                     .classed('related', false);
55094
55095                 context.map().on('drawn.select', null);
55096                 context.ui().sidebar.hide();
55097                 context.features().forceVisible([]);
55098
55099                 var entity = singular();
55100                 if (_newFeature && entity && entity.type === 'relation' &&
55101                     // no tags
55102                     Object.keys(entity.tags).length === 0 &&
55103                     // no parent relations
55104                     context.graph().parentRelations(entity).length === 0 &&
55105                     // no members or one member with no role
55106                     (entity.members.length === 0 || (entity.members.length === 1 && !entity.members[0].role))
55107                 ) {
55108                     // the user added this relation but didn't edit it at all, so just delete it
55109                     var deleteAction = actionDeleteRelation(entity.id, true /* don't delete untagged members */);
55110                     context.perform(deleteAction, _t('operations.delete.annotation.relation'));
55111                 }
55112             };
55113
55114
55115             return mode;
55116         }
55117
55118         function uiLasso(context) {
55119             var group, polygon;
55120
55121             lasso.coordinates = [];
55122
55123             function lasso(selection) {
55124                 context.container()
55125                     .classed('lasso', true);
55126
55127                 group = selection
55128                     .append('g')
55129                     .attr('class', 'lasso hide');
55130
55131                 polygon = group
55132                     .append('path')
55133                     .attr('class', 'lasso-path');
55134
55135                 group
55136                     .call(uiToggle(true));
55137             }
55138
55139
55140             function draw() {
55141                 if (polygon) {
55142                     polygon.data([lasso.coordinates])
55143                         .attr('d', function(d) { return 'M' + d.join(' L') + ' Z'; });
55144                 }
55145             }
55146
55147
55148             lasso.extent = function () {
55149                 return lasso.coordinates.reduce(function(extent, point) {
55150                     return extent.extend(geoExtent(point));
55151                 }, geoExtent());
55152             };
55153
55154
55155             lasso.p = function(_) {
55156                 if (!arguments.length) { return lasso; }
55157                 lasso.coordinates.push(_);
55158                 draw();
55159                 return lasso;
55160             };
55161
55162
55163             lasso.close = function() {
55164                 if (group) {
55165                     group.call(uiToggle(false, function() {
55166                         select(this).remove();
55167                     }));
55168                 }
55169                 context.container().classed('lasso', false);
55170             };
55171
55172
55173             return lasso;
55174         }
55175
55176         function behaviorLasso(context) {
55177
55178             // use pointer events on supported platforms; fallback to mouse events
55179             var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
55180
55181             var behavior = function(selection) {
55182                 var lasso;
55183
55184
55185                 function pointerdown() {
55186                     var button = 0;  // left
55187                     if (event.button === button && event.shiftKey === true) {
55188                         lasso = null;
55189
55190                         select(window)
55191                             .on(_pointerPrefix + 'move.lasso', pointermove)
55192                             .on(_pointerPrefix + 'up.lasso', pointerup);
55193
55194                         event.stopPropagation();
55195                     }
55196                 }
55197
55198
55199                 function pointermove() {
55200                     if (!lasso) {
55201                         lasso = uiLasso(context);
55202                         context.surface().call(lasso);
55203                     }
55204
55205                     lasso.p(context.map().mouse());
55206                 }
55207
55208
55209                 function normalize(a, b) {
55210                     return [
55211                         [Math.min(a[0], b[0]), Math.min(a[1], b[1])],
55212                         [Math.max(a[0], b[0]), Math.max(a[1], b[1])]
55213                     ];
55214                 }
55215
55216
55217                 function lassoed() {
55218                     if (!lasso) { return []; }
55219
55220                     var graph = context.graph();
55221                     var limitToNodes;
55222
55223                     if (context.map().editableDataEnabled(true /* skipZoomCheck */) && context.map().isInWideSelection()) {
55224                         // only select from the visible nodes
55225                         limitToNodes = new Set(utilGetAllNodes(context.selectedIDs(), graph));
55226                     } else if (!context.map().editableDataEnabled()) {
55227                         return [];
55228                     }
55229
55230                     var bounds = lasso.extent().map(context.projection.invert);
55231                     var extent = geoExtent(normalize(bounds[0], bounds[1]));
55232
55233                     var intersects = context.history().intersects(extent).filter(function(entity) {
55234                         return entity.type === 'node' &&
55235                             (!limitToNodes || limitToNodes.has(entity)) &&
55236                             geoPointInPolygon(context.projection(entity.loc), lasso.coordinates) &&
55237                             !context.features().isHidden(entity, graph, entity.geometry(graph));
55238                     });
55239
55240                     // sort the lassoed nodes as best we can
55241                     intersects.sort(function(node1, node2) {
55242                         var parents1 = graph.parentWays(node1);
55243                         var parents2 = graph.parentWays(node2);
55244                         if (parents1.length && parents2.length) {
55245                             // both nodes are vertices
55246
55247                             var sharedParents = utilArrayIntersection(parents1, parents2);
55248                             if (sharedParents.length) {
55249                                 var sharedParentNodes = sharedParents[0].nodes;
55250                                 // vertices are members of the same way; sort them in their listed order
55251                                 return sharedParentNodes.indexOf(node1.id) -
55252                                     sharedParentNodes.indexOf(node2.id);
55253                             } else {
55254                                 // vertices do not share a way; group them by their respective parent ways
55255                                 return parseFloat(parents1[0].id.slice(1)) -
55256                                     parseFloat(parents2[0].id.slice(1));
55257                             }
55258
55259                         } else if (parents1.length || parents2.length) {
55260                             // only one node is a vertex; sort standalone points before vertices
55261                             return parents1.length - parents2.length;
55262                         }
55263                         // both nodes are standalone points; sort left to right
55264                         return node1.loc[0] - node2.loc[0];
55265                     });
55266
55267                     return intersects.map(function(entity) { return entity.id; });
55268                 }
55269
55270
55271                 function pointerup() {
55272                     select(window)
55273                         .on(_pointerPrefix + 'move.lasso', null)
55274                         .on(_pointerPrefix + 'up.lasso', null);
55275
55276                     if (!lasso) { return; }
55277
55278                     var ids = lassoed();
55279                     lasso.close();
55280
55281                     if (ids.length) {
55282                         context.enter(modeSelect(context, ids));
55283                     }
55284                 }
55285
55286                 selection
55287                     .on(_pointerPrefix + 'down.lasso', pointerdown);
55288             };
55289
55290
55291             behavior.off = function(selection) {
55292                 selection.on(_pointerPrefix + 'down.lasso', null);
55293             };
55294
55295
55296             return behavior;
55297         }
55298
55299         function modeBrowse(context) {
55300             var mode = {
55301                 button: 'browse',
55302                 id: 'browse',
55303                 title: _t('modes.browse.title'),
55304                 description: _t('modes.browse.description')
55305             };
55306             var sidebar;
55307
55308             var _selectBehavior;
55309             var _behaviors = [];
55310
55311
55312             mode.selectBehavior = function(val) {
55313                 if (!arguments.length) { return _selectBehavior; }
55314                 _selectBehavior = val;
55315                 return mode;
55316             };
55317
55318
55319             mode.enter = function() {
55320                 if (!_behaviors.length) {
55321                     if (!_selectBehavior) { _selectBehavior = behaviorSelect(context); }
55322                     _behaviors = [
55323                         behaviorPaste(context),
55324                         behaviorHover(context).on('hover', context.ui().sidebar.hover),
55325                         _selectBehavior,
55326                         behaviorLasso(context),
55327                         modeDragNode(context).behavior,
55328                         modeDragNote(context).behavior
55329                     ];
55330                 }
55331                 _behaviors.forEach(context.install);
55332
55333                 // Get focus on the body.
55334                 if (document.activeElement && document.activeElement.blur) {
55335                     document.activeElement.blur();
55336                 }
55337
55338                 if (sidebar) {
55339                     context.ui().sidebar.show(sidebar);
55340                 } else {
55341                     context.ui().sidebar.select(null);
55342                 }
55343             };
55344
55345
55346             mode.exit = function() {
55347                 context.ui().sidebar.hover.cancel();
55348                 _behaviors.forEach(context.uninstall);
55349
55350                 if (sidebar) {
55351                     context.ui().sidebar.hide();
55352                 }
55353             };
55354
55355
55356             mode.sidebar = function(_) {
55357                 if (!arguments.length) { return sidebar; }
55358                 sidebar = _;
55359                 return mode;
55360             };
55361
55362
55363             mode.operations = function() {
55364                 return [operationPaste(context)];
55365             };
55366
55367
55368             return mode;
55369         }
55370
55371         function behaviorAddWay(context) {
55372             var dispatch$1 = dispatch('start', 'startFromWay', 'startFromNode');
55373             var draw = behaviorDraw(context);
55374
55375             function behavior(surface) {
55376                 draw.on('click', function() { dispatch$1.apply('start', this, arguments); })
55377                     .on('clickWay', function() { dispatch$1.apply('startFromWay', this, arguments); })
55378                     .on('clickNode', function() { dispatch$1.apply('startFromNode', this, arguments); })
55379                     .on('cancel', behavior.cancel)
55380                     .on('finish', behavior.cancel);
55381
55382                 context.map()
55383                     .dblclickZoomEnable(false);
55384
55385                 surface.call(draw);
55386             }
55387
55388
55389             behavior.off = function(surface) {
55390                 surface.call(draw.off);
55391             };
55392
55393
55394             behavior.cancel = function() {
55395                 window.setTimeout(function() {
55396                     context.map().dblclickZoomEnable(true);
55397                 }, 1000);
55398
55399                 context.enter(modeBrowse(context));
55400             };
55401
55402
55403             return utilRebind(behavior, dispatch$1, 'on');
55404         }
55405
55406         function behaviorHash(context) {
55407
55408             // cached window.location.hash
55409             var _cachedHash = null;
55410             // allowable latitude range
55411             var _latitudeLimit = 90 - 1e-8;
55412
55413             function computedHashParameters() {
55414                 var map = context.map();
55415                 var center = map.center();
55416                 var zoom = map.zoom();
55417                 var precision = Math.max(0, Math.ceil(Math.log(zoom) / Math.LN2));
55418                 var oldParams = utilObjectOmit(utilStringQs(window.location.hash),
55419                     ['comment', 'source', 'hashtags', 'walkthrough']
55420                 );
55421                 var newParams = {};
55422
55423                 delete oldParams.id;
55424                 var selected = context.selectedIDs().filter(function(id) {
55425                     return context.hasEntity(id);
55426                 });
55427                 if (selected.length) {
55428                     newParams.id = selected.join(',');
55429                 }
55430
55431                 newParams.map = zoom.toFixed(2) +
55432                     '/' + center[1].toFixed(precision) +
55433                     '/' + center[0].toFixed(precision);
55434
55435                 return Object.assign(oldParams, newParams);
55436             }
55437
55438             function computedHash() {
55439                 return '#' + utilQsString(computedHashParameters(), true);
55440             }
55441
55442             function computedTitle(includeChangeCount) {
55443
55444                 var baseTitle = context.documentTitleBase() || 'iD';
55445                 var contextual;
55446                 var changeCount;
55447                 var titleID;
55448
55449                 var selected = context.selectedIDs().filter(function(id) {
55450                     return context.hasEntity(id);
55451                 });
55452                 if (selected.length) {
55453                     var firstLabel = utilDisplayLabel(context.entity(selected[0]), context.graph());
55454                     if (selected.length > 1 ) {
55455                         contextual = _t('title.labeled_and_more', {
55456                             labeled: firstLabel,
55457                             count: (selected.length - 1).toString()
55458                         });
55459                     } else {
55460                         contextual = firstLabel;
55461                     }
55462                     titleID = 'context';
55463                 }
55464
55465                 if (includeChangeCount) {
55466                     changeCount = context.history().difference().summary().length;
55467                     if (changeCount > 0) {
55468                         titleID = contextual ? 'changes_context' : 'changes';
55469                     }
55470                 }
55471
55472                 if (titleID) {
55473                     return _t('title.format.' + titleID, {
55474                         changes: changeCount,
55475                         base: baseTitle,
55476                         context: contextual
55477                     });
55478                 }
55479
55480                 return baseTitle;
55481             }
55482
55483             function updateTitle(includeChangeCount) {
55484                 if (!context.setsDocumentTitle()) { return; }
55485
55486                 var newTitle = computedTitle(includeChangeCount);
55487                 if (document.title !== newTitle) {
55488                     document.title = newTitle;
55489                 }
55490             }
55491
55492             function updateHashIfNeeded() {
55493                 if (context.inIntro()) { return; }
55494
55495                 var latestHash = computedHash();
55496                 if (_cachedHash !== latestHash) {
55497                     _cachedHash = latestHash;
55498
55499                     // Update the URL hash without affecting the browser navigation stack,
55500                     // though unavoidably creating a browser history entry
55501                     window.history.replaceState(null, computedTitle(false /* includeChangeCount */), latestHash);
55502
55503                     // set the title we want displayed for the browser tab/window
55504                     updateTitle(true /* includeChangeCount */);
55505                 }
55506             }
55507
55508             var _throttledUpdate = throttle(updateHashIfNeeded, 500);
55509             var _throttledUpdateTitle = throttle(function() {
55510                 updateTitle(true /* includeChangeCount */);
55511             }, 500);
55512
55513             function hashchange() {
55514
55515                 // ignore spurious hashchange events
55516                 if (window.location.hash === _cachedHash) { return; }
55517
55518                 _cachedHash = window.location.hash;
55519
55520                 var q = utilStringQs(_cachedHash);
55521                 var mapArgs = (q.map || '').split('/').map(Number);
55522
55523                 if (mapArgs.length < 3 || mapArgs.some(isNaN)) {
55524                     // replace bogus hash
55525                     updateHashIfNeeded();
55526
55527                 } else {
55528                     // don't update if the new hash already reflects the state of iD
55529                     if (_cachedHash === computedHash()) { return; }
55530
55531                     var mode = context.mode();
55532
55533                     context.map().centerZoom([mapArgs[2], Math.min(_latitudeLimit, Math.max(-_latitudeLimit, mapArgs[1]))], mapArgs[0]);
55534
55535                     if (q.id) {
55536                         var ids = q.id.split(',').filter(function(id) {
55537                             return context.hasEntity(id);
55538                         });
55539                         var skip = mode && mode.id === 'select' && utilArrayIdentical(mode.selectedIDs(), ids);
55540                         if (ids.length && !skip) {
55541                             context.enter(modeSelect(context, ids));
55542                             return;
55543                         }
55544                     }
55545
55546                     var center = context.map().center();
55547                     var dist = geoSphericalDistance(center, [mapArgs[2], mapArgs[1]]);
55548                     var maxdist = 500;
55549
55550                     // Don't allow the hash location to change too much while drawing
55551                     // This can happen if the user accidentally hit the back button.  #3996
55552                     if (mode && mode.id.match(/^draw/) !== null && dist > maxdist) {
55553                         context.enter(modeBrowse(context));
55554                         return;
55555                     }
55556                 }
55557             }
55558
55559             function behavior() {
55560                 context.map()
55561                     .on('move.behaviorHash', _throttledUpdate);
55562
55563                 context.history()
55564                     .on('change.behaviorHash', _throttledUpdateTitle);
55565
55566                 context
55567                     .on('enter.behaviorHash', _throttledUpdate);
55568
55569                 select(window)
55570                     .on('hashchange.behaviorHash', hashchange);
55571
55572                 if (window.location.hash) {
55573                     var q = utilStringQs(window.location.hash);
55574
55575                     if (q.id) {
55576                         //if (!context.history().hasRestorableChanges()) {
55577                             // targeting specific features: download, select, and zoom to them
55578                             context.zoomToEntity(q.id.split(',')[0], !q.map);
55579                         //}
55580                     }
55581
55582                     if (q.walkthrough === 'true') {
55583                         behavior.startWalkthrough = true;
55584                     }
55585
55586                     if (q.map) {
55587                         behavior.hadHash = true;
55588                     }
55589
55590                     hashchange();
55591
55592                     updateTitle(false);
55593                 }
55594             }
55595
55596             behavior.off = function() {
55597                 _throttledUpdate.cancel();
55598                 _throttledUpdateTitle.cancel();
55599
55600                 context.map()
55601                     .on('move.behaviorHash', null);
55602
55603                 context
55604                     .on('enter.behaviorHash', null);
55605
55606                 select(window)
55607                     .on('hashchange.behaviorHash', null);
55608
55609                 window.location.hash = '';
55610             };
55611
55612             return behavior;
55613         }
55614
55615         /*
55616             iD.coreDifference represents the difference between two graphs.
55617             It knows how to calculate the set of entities that were
55618             created, modified, or deleted, and also contains the logic
55619             for recursively extending a difference to the complete set
55620             of entities that will require a redraw, taking into account
55621             child and parent relationships.
55622          */
55623         function coreDifference(base, head) {
55624             var _changes = {};
55625             var _didChange = {};  // 'addition', 'deletion', 'geometry', 'properties'
55626             var _diff = {};
55627
55628             function checkEntityID(id) {
55629                 var h = head.entities[id];
55630                 var b = base.entities[id];
55631
55632                 if (h === b) { return; }
55633                 if (_changes[id]) { return; }
55634
55635                 if (!h && b) {
55636                     _changes[id] = { base: b, head: h };
55637                     _didChange.deletion = true;
55638                     return;
55639                 }
55640                 if (h && !b) {
55641                     _changes[id] = { base: b, head: h };
55642                     _didChange.addition = true;
55643                     return;
55644                 }
55645
55646                 if (h && b) {
55647                     if (h.members && b.members && !fastDeepEqual(h.members, b.members)) {
55648                         _changes[id] = { base: b, head: h };
55649                         _didChange.geometry = true;
55650                         _didChange.properties = true;
55651                         return;
55652                     }
55653                     if (h.loc && b.loc && !geoVecEqual(h.loc, b.loc)) {
55654                         _changes[id] = { base: b, head: h };
55655                         _didChange.geometry = true;
55656                     }
55657                     if (h.nodes && b.nodes && !fastDeepEqual(h.nodes, b.nodes)) {
55658                         _changes[id] = { base: b, head: h };
55659                         _didChange.geometry = true;
55660                     }
55661                     if (h.tags && b.tags && !fastDeepEqual(h.tags, b.tags)) {
55662                         _changes[id] = { base: b, head: h };
55663                         _didChange.properties = true;
55664                     }
55665                 }
55666             }
55667
55668             function load() {
55669                 // HOT CODE: there can be many thousands of downloaded entities, so looping
55670                 // through them all can become a performance bottleneck. Optimize by
55671                 // resolving duplicates and using a basic `for` loop
55672                 var ids = utilArrayUniq(Object.keys(head.entities).concat(Object.keys(base.entities)));
55673                 for (var i = 0; i < ids.length; i++) {
55674                     checkEntityID(ids[i]);
55675                 }
55676             }
55677             load();
55678
55679
55680             _diff.length = function length() {
55681                 return Object.keys(_changes).length;
55682             };
55683
55684
55685             _diff.changes = function changes() {
55686                 return _changes;
55687             };
55688
55689             _diff.didChange = _didChange;
55690
55691
55692             // pass true to include affected relation members
55693             _diff.extantIDs = function extantIDs(includeRelMembers) {
55694                 var result = new Set();
55695                 Object.keys(_changes).forEach(function(id) {
55696                     if (_changes[id].head) {
55697                         result.add(id);
55698                     }
55699
55700                     var h = _changes[id].head;
55701                     var b = _changes[id].base;
55702                     var entity = h || b;
55703
55704                     if (includeRelMembers && entity.type === 'relation') {
55705                         var mh = h ? h.members.map(function(m) { return m.id; }) : [];
55706                         var mb = b ? b.members.map(function(m) { return m.id; }) : [];
55707                         utilArrayUnion(mh, mb).forEach(function(memberID) {
55708                             if (head.hasEntity(memberID)) {
55709                                 result.add(memberID);
55710                             }
55711                         });
55712                     }
55713                 });
55714
55715                 return Array.from(result);
55716             };
55717
55718
55719             _diff.modified = function modified() {
55720                 var result = [];
55721                 Object.values(_changes).forEach(function(change) {
55722                     if (change.base && change.head) {
55723                         result.push(change.head);
55724                     }
55725                 });
55726                 return result;
55727             };
55728
55729
55730             _diff.created = function created() {
55731                 var result = [];
55732                 Object.values(_changes).forEach(function(change) {
55733                     if (!change.base && change.head) {
55734                         result.push(change.head);
55735                     }
55736                 });
55737                 return result;
55738             };
55739
55740
55741             _diff.deleted = function deleted() {
55742                 var result = [];
55743                 Object.values(_changes).forEach(function(change) {
55744                     if (change.base && !change.head) {
55745                         result.push(change.base);
55746                     }
55747                 });
55748                 return result;
55749             };
55750
55751
55752             _diff.summary = function summary() {
55753                 var relevant = {};
55754
55755                 var keys = Object.keys(_changes);
55756                 for (var i = 0; i < keys.length; i++) {
55757                     var change = _changes[keys[i]];
55758
55759                     if (change.head && change.head.geometry(head) !== 'vertex') {
55760                         addEntity(change.head, head, change.base ? 'modified' : 'created');
55761
55762                     } else if (change.base && change.base.geometry(base) !== 'vertex') {
55763                         addEntity(change.base, base, 'deleted');
55764
55765                     } else if (change.base && change.head) { // modified vertex
55766                         var moved    = !fastDeepEqual(change.base.loc,  change.head.loc);
55767                         var retagged = !fastDeepEqual(change.base.tags, change.head.tags);
55768
55769                         if (moved) {
55770                             addParents(change.head);
55771                         }
55772
55773                         if (retagged || (moved && change.head.hasInterestingTags())) {
55774                             addEntity(change.head, head, 'modified');
55775                         }
55776
55777                     } else if (change.head && change.head.hasInterestingTags()) { // created vertex
55778                         addEntity(change.head, head, 'created');
55779
55780                     } else if (change.base && change.base.hasInterestingTags()) { // deleted vertex
55781                         addEntity(change.base, base, 'deleted');
55782                     }
55783                 }
55784
55785                 return Object.values(relevant);
55786
55787
55788                 function addEntity(entity, graph, changeType) {
55789                     relevant[entity.id] = {
55790                         entity: entity,
55791                         graph: graph,
55792                         changeType: changeType
55793                     };
55794                 }
55795
55796                 function addParents(entity) {
55797                     var parents = head.parentWays(entity);
55798                     for (var j = parents.length - 1; j >= 0; j--) {
55799                         var parent = parents[j];
55800                         if (!(parent.id in relevant)) {
55801                             addEntity(parent, head, 'modified');
55802                         }
55803                     }
55804                 }
55805             };
55806
55807
55808             // returns complete set of entities that require a redraw
55809             //  (optionally within given `extent`)
55810             _diff.complete = function complete(extent) {
55811                 var result = {};
55812                 var id, change;
55813
55814                 for (id in _changes) {
55815                     change = _changes[id];
55816
55817                     var h = change.head;
55818                     var b = change.base;
55819                     var entity = h || b;
55820                     var i;
55821
55822                     if (extent &&
55823                         (!h || !h.intersects(extent, head)) &&
55824                         (!b || !b.intersects(extent, base)))
55825                         { continue; }
55826
55827                     result[id] = h;
55828
55829                     if (entity.type === 'way') {
55830                         var nh = h ? h.nodes : [];
55831                         var nb = b ? b.nodes : [];
55832                         var diff;
55833
55834                         diff = utilArrayDifference(nh, nb);
55835                         for (i = 0; i < diff.length; i++) {
55836                             result[diff[i]] = head.hasEntity(diff[i]);
55837                         }
55838
55839                         diff = utilArrayDifference(nb, nh);
55840                         for (i = 0; i < diff.length; i++) {
55841                             result[diff[i]] = head.hasEntity(diff[i]);
55842                         }
55843                     }
55844
55845                     if (entity.type === 'relation' && entity.isMultipolygon()) {
55846                         var mh = h ? h.members.map(function(m) { return m.id; }) : [];
55847                         var mb = b ? b.members.map(function(m) { return m.id; }) : [];
55848                         var ids = utilArrayUnion(mh, mb);
55849                         for (i = 0; i < ids.length; i++) {
55850                             var member = head.hasEntity(ids[i]);
55851                             if (!member) { continue; }   // not downloaded
55852                             if (extent && !member.intersects(extent, head)) { continue; }   // not visible
55853                             result[ids[i]] = member;
55854                         }
55855                     }
55856
55857                     addParents(head.parentWays(entity), result);
55858                     addParents(head.parentRelations(entity), result);
55859                 }
55860
55861                 return result;
55862
55863
55864                 function addParents(parents, result) {
55865                     for (var i = 0; i < parents.length; i++) {
55866                         var parent = parents[i];
55867                         if (parent.id in result) { continue; }
55868
55869                         result[parent.id] = parent;
55870                         addParents(head.parentRelations(parent), result);
55871                     }
55872                 }
55873             };
55874
55875
55876             return _diff;
55877         }
55878
55879         function coreTree(head) {
55880             // tree for entities
55881             var _rtree = new RBush();
55882             var _bboxes = {};
55883
55884             // maintain a separate tree for granular way segments
55885             var _segmentsRTree = new RBush();
55886             var _segmentsBBoxes = {};
55887             var _segmentsByWayId = {};
55888
55889             var tree = {};
55890
55891
55892             function entityBBox(entity) {
55893                 var bbox = entity.extent(head).bbox();
55894                 bbox.id = entity.id;
55895                 _bboxes[entity.id] = bbox;
55896                 return bbox;
55897             }
55898
55899
55900             function segmentBBox(segment) {
55901                 var extent = segment.extent(head);
55902                 // extent can be null if the node entities aren't in the graph for some reason
55903                 if (!extent) { return null; }
55904
55905                 var bbox = extent.bbox();
55906                 bbox.segment = segment;
55907                 _segmentsBBoxes[segment.id] = bbox;
55908                 return bbox;
55909             }
55910
55911
55912             function removeEntity(entity) {
55913                 _rtree.remove(_bboxes[entity.id]);
55914                 delete _bboxes[entity.id];
55915
55916                 if (_segmentsByWayId[entity.id]) {
55917                     _segmentsByWayId[entity.id].forEach(function(segment) {
55918                         _segmentsRTree.remove(_segmentsBBoxes[segment.id]);
55919                         delete _segmentsBBoxes[segment.id];
55920                     });
55921                     delete _segmentsByWayId[entity.id];
55922                 }
55923             }
55924
55925
55926             function loadEntities(entities) {
55927                 _rtree.load(entities.map(entityBBox));
55928
55929                 var segments = [];
55930                 entities.forEach(function(entity) {
55931                     if (entity.segments) {
55932                         var entitySegments = entity.segments(head);
55933                         // cache these to make them easy to remove later
55934                         _segmentsByWayId[entity.id] = entitySegments;
55935                         segments = segments.concat(entitySegments);
55936                     }
55937                 });
55938                 if (segments.length) { _segmentsRTree.load(segments.map(segmentBBox).filter(Boolean)); }
55939             }
55940
55941
55942             function updateParents(entity, insertions, memo) {
55943                 head.parentWays(entity).forEach(function(way) {
55944                     if (_bboxes[way.id]) {
55945                         removeEntity(way);
55946                         insertions[way.id] = way;
55947                     }
55948                     updateParents(way, insertions, memo);
55949                 });
55950
55951                 head.parentRelations(entity).forEach(function(relation) {
55952                     if (memo[entity.id]) { return; }
55953                     memo[entity.id] = true;
55954                     if (_bboxes[relation.id]) {
55955                         removeEntity(relation);
55956                         insertions[relation.id] = relation;
55957                     }
55958                     updateParents(relation, insertions, memo);
55959                 });
55960             }
55961
55962
55963             tree.rebase = function(entities, force) {
55964                 var insertions = {};
55965
55966                 for (var i = 0; i < entities.length; i++) {
55967                     var entity = entities[i];
55968                     if (!entity.visible) { continue; }
55969
55970                     if (head.entities.hasOwnProperty(entity.id) || _bboxes[entity.id]) {
55971                         if (!force) {
55972                             continue;
55973                         } else if (_bboxes[entity.id]) {
55974                             removeEntity(entity);
55975                         }
55976                     }
55977
55978                     insertions[entity.id] = entity;
55979                     updateParents(entity, insertions, {});
55980                 }
55981
55982                 loadEntities(Object.values(insertions));
55983
55984                 return tree;
55985             };
55986
55987
55988             function updateToGraph(graph) {
55989                 if (graph === head) { return; }
55990
55991                 var diff = coreDifference(head, graph);
55992
55993                 head = graph;
55994
55995                 var changed = diff.didChange;
55996                 if (!changed.addition && !changed.deletion && !changed.geometry) { return; }
55997
55998                 var insertions = {};
55999
56000                 if (changed.deletion) {
56001                     diff.deleted().forEach(function(entity) {
56002                         removeEntity(entity);
56003                     });
56004                 }
56005
56006                 if (changed.geometry) {
56007                     diff.modified().forEach(function(entity) {
56008                         removeEntity(entity);
56009                         insertions[entity.id] = entity;
56010                         updateParents(entity, insertions, {});
56011                     });
56012                 }
56013
56014                 if (changed.addition) {
56015                     diff.created().forEach(function(entity) {
56016                         insertions[entity.id] = entity;
56017                     });
56018                 }
56019
56020                 loadEntities(Object.values(insertions));
56021             }
56022
56023             // returns an array of entities with bounding boxes overlapping `extent` for the given `graph`
56024             tree.intersects = function(extent, graph) {
56025                 updateToGraph(graph);
56026                 return _rtree.search(extent.bbox())
56027                     .map(function(bbox) { return graph.entity(bbox.id); });
56028             };
56029
56030             // returns an array of segment objects with bounding boxes overlapping `extent` for the given `graph`
56031             tree.waySegments = function(extent, graph) {
56032                 updateToGraph(graph);
56033                 return _segmentsRTree.search(extent.bbox())
56034                     .map(function(bbox) { return bbox.segment; });
56035             };
56036
56037
56038             return tree;
56039         }
56040
56041         function uiModal(selection, blocking) {
56042           var this$1 = this;
56043
56044           var keybinding = utilKeybinding('modal');
56045           var previous = selection.select('div.modal');
56046           var animate = previous.empty();
56047
56048           previous.transition()
56049             .duration(200)
56050             .style('opacity', 0)
56051             .remove();
56052
56053           var shaded = selection
56054             .append('div')
56055             .attr('class', 'shaded')
56056             .style('opacity', 0);
56057
56058           shaded.close = function () {
56059             shaded
56060               .transition()
56061               .duration(200)
56062               .style('opacity',0)
56063               .remove();
56064
56065             modal
56066               .transition()
56067               .duration(200)
56068               .style('top','0px');
56069
56070             select(document)
56071               .call(keybinding.unbind);
56072           };
56073
56074
56075           var modal = shaded
56076             .append('div')
56077             .attr('class', 'modal fillL');
56078
56079           if (!blocking) {
56080             shaded.on('click.remove-modal', function () {
56081               if (event.target === this$1) {
56082                 shaded.close();
56083               }
56084             });
56085
56086             modal
56087               .append('button')
56088               .attr('class', 'close')
56089               .on('click', shaded.close)
56090               .call(svgIcon('#iD-icon-close'));
56091
56092             keybinding
56093               .on('⌫', shaded.close)
56094               .on('⎋', shaded.close);
56095
56096             select(document)
56097               .call(keybinding);
56098           }
56099
56100           modal
56101             .append('div')
56102             .attr('class', 'content');
56103
56104           if (animate) {
56105             shaded.transition().style('opacity', 1);
56106           } else {
56107             shaded.style('opacity', 1);
56108           }
56109
56110           return shaded;
56111         }
56112
56113         function uiLoading(context) {
56114           var _modalSelection = select(null);
56115           var _message = '';
56116           var _blocking = false;
56117
56118
56119           var loading = function (selection) {
56120             _modalSelection = uiModal(selection, _blocking);
56121
56122             var loadertext = _modalSelection.select('.content')
56123               .classed('loading-modal', true)
56124               .append('div')
56125               .attr('class', 'modal-section fillL');
56126
56127             loadertext
56128               .append('img')
56129               .attr('class', 'loader')
56130               .attr('src', context.imagePath('loader-white.gif'));
56131
56132             loadertext
56133               .append('h3')
56134               .text(_message);
56135
56136             _modalSelection.select('button.close')
56137               .attr('class', 'hide');
56138
56139             return loading;
56140           };
56141
56142
56143           loading.message = function(val) {
56144             if (!arguments.length) { return _message; }
56145             _message = val;
56146             return loading;
56147           };
56148
56149
56150           loading.blocking = function(val) {
56151             if (!arguments.length) { return _blocking; }
56152             _blocking = val;
56153             return loading;
56154           };
56155
56156
56157           loading.close = function () {
56158             _modalSelection.remove();
56159           };
56160
56161
56162           loading.isShown = function () {
56163             return _modalSelection && !_modalSelection.empty() && _modalSelection.node().parentNode;
56164           };
56165
56166
56167           return loading;
56168         }
56169
56170         function coreHistory(context) {
56171             var dispatch$1 = dispatch('change', 'merge', 'restore', 'undone', 'redone');
56172             var lock = utilSessionMutex('lock');
56173
56174             // restorable if iD not open in another window/tab and a saved history exists in localStorage
56175             var _hasUnresolvedRestorableChanges = lock.lock() && !!corePreferences(getKey('saved_history'));
56176
56177             var duration = 150;
56178             var _imageryUsed = [];
56179             var _photoOverlaysUsed = [];
56180             var _checkpoints = {};
56181             var _pausedGraph;
56182             var _stack;
56183             var _index;
56184             var _tree;
56185
56186
56187             // internal _act, accepts list of actions and eased time
56188             function _act(actions, t) {
56189                 actions = Array.prototype.slice.call(actions);
56190
56191                 var annotation;
56192                 if (typeof actions[actions.length - 1] !== 'function') {
56193                     annotation = actions.pop();
56194                 }
56195
56196                 var graph = _stack[_index].graph;
56197                 for (var i = 0; i < actions.length; i++) {
56198                     graph = actions[i](graph, t);
56199                 }
56200
56201                 return {
56202                     graph: graph,
56203                     annotation: annotation,
56204                     imageryUsed: _imageryUsed,
56205                     photoOverlaysUsed: _photoOverlaysUsed,
56206                     transform: context.projection.transform(),
56207                     selectedIDs: context.selectedIDs()
56208                 };
56209             }
56210
56211
56212             // internal _perform with eased time
56213             function _perform(args, t) {
56214                 var previous = _stack[_index].graph;
56215                 _stack = _stack.slice(0, _index + 1);
56216                 var actionResult = _act(args, t);
56217                 _stack.push(actionResult);
56218                 _index++;
56219                 return change(previous);
56220             }
56221
56222
56223             // internal _replace with eased time
56224             function _replace(args, t) {
56225                 var previous = _stack[_index].graph;
56226                 // assert(_index == _stack.length - 1)
56227                 var actionResult = _act(args, t);
56228                 _stack[_index] = actionResult;
56229                 return change(previous);
56230             }
56231
56232
56233             // internal _overwrite with eased time
56234             function _overwrite(args, t) {
56235                 var previous = _stack[_index].graph;
56236                 if (_index > 0) {
56237                     _index--;
56238                     _stack.pop();
56239                 }
56240                 _stack = _stack.slice(0, _index + 1);
56241                 var actionResult = _act(args, t);
56242                 _stack.push(actionResult);
56243                 _index++;
56244                 return change(previous);
56245             }
56246
56247
56248             // determine difference and dispatch a change event
56249             function change(previous) {
56250                 var difference = coreDifference(previous, history.graph());
56251                 if (!_pausedGraph) {
56252                     dispatch$1.call('change', this, difference);
56253                 }
56254                 return difference;
56255             }
56256
56257
56258             // iD uses namespaced keys so multiple installations do not conflict
56259             function getKey(n) {
56260                 return 'iD_' + window.location.origin + '_' + n;
56261             }
56262
56263
56264             var history = {
56265
56266                 graph: function() {
56267                     return _stack[_index].graph;
56268                 },
56269
56270
56271                 tree: function() {
56272                     return _tree;
56273                 },
56274
56275
56276                 base: function() {
56277                     return _stack[0].graph;
56278                 },
56279
56280
56281                 merge: function(entities/*, extent*/) {
56282                     var stack = _stack.map(function(state) { return state.graph; });
56283                     _stack[0].graph.rebase(entities, stack, false);
56284                     _tree.rebase(entities, false);
56285
56286                     dispatch$1.call('merge', this, entities);
56287                 },
56288
56289
56290                 perform: function() {
56291                     // complete any transition already in progress
56292                     select(document).interrupt('history.perform');
56293
56294                     var transitionable = false;
56295                     var action0 = arguments[0];
56296
56297                     if (arguments.length === 1 ||
56298                         (arguments.length === 2 && (typeof arguments[1] !== 'function'))) {
56299                         transitionable = !!action0.transitionable;
56300                     }
56301
56302                     if (transitionable) {
56303                         var origArguments = arguments;
56304                         select(document)
56305                             .transition('history.perform')
56306                             .duration(duration)
56307                             .ease(linear$1)
56308                             .tween('history.tween', function() {
56309                                 return function(t) {
56310                                     if (t < 1) { _overwrite([action0], t); }
56311                                 };
56312                             })
56313                             .on('start', function() {
56314                                 _perform([action0], 0);
56315                             })
56316                             .on('end interrupt', function() {
56317                                 _overwrite(origArguments, 1);
56318                             });
56319
56320                     } else {
56321                         return _perform(arguments);
56322                     }
56323                 },
56324
56325
56326                 replace: function() {
56327                     select(document).interrupt('history.perform');
56328                     return _replace(arguments, 1);
56329                 },
56330
56331
56332                 // Same as calling pop and then perform
56333                 overwrite: function() {
56334                     select(document).interrupt('history.perform');
56335                     return _overwrite(arguments, 1);
56336                 },
56337
56338
56339                 pop: function(n) {
56340                     select(document).interrupt('history.perform');
56341
56342                     var previous = _stack[_index].graph;
56343                     if (isNaN(+n) || +n < 0) {
56344                         n = 1;
56345                     }
56346                     while (n-- > 0 && _index > 0) {
56347                         _index--;
56348                         _stack.pop();
56349                     }
56350                     return change(previous);
56351                 },
56352
56353
56354                 // Back to the previous annotated state or _index = 0.
56355                 undo: function() {
56356                     select(document).interrupt('history.perform');
56357
56358                     var previousStack = _stack[_index];
56359                     var previous = previousStack.graph;
56360                     while (_index > 0) {
56361                         _index--;
56362                         if (_stack[_index].annotation) { break; }
56363                     }
56364
56365                     dispatch$1.call('undone', this, _stack[_index], previousStack);
56366                     return change(previous);
56367                 },
56368
56369
56370                 // Forward to the next annotated state.
56371                 redo: function() {
56372                     select(document).interrupt('history.perform');
56373
56374                     var previousStack = _stack[_index];
56375                     var previous = previousStack.graph;
56376                     var tryIndex = _index;
56377                     while (tryIndex < _stack.length - 1) {
56378                         tryIndex++;
56379                         if (_stack[tryIndex].annotation) {
56380                             _index = tryIndex;
56381                             dispatch$1.call('redone', this, _stack[_index], previousStack);
56382                             break;
56383                         }
56384                     }
56385
56386                     return change(previous);
56387                 },
56388
56389
56390                 pauseChangeDispatch: function() {
56391                     if (!_pausedGraph) {
56392                         _pausedGraph = _stack[_index].graph;
56393                     }
56394                 },
56395
56396
56397                 resumeChangeDispatch: function() {
56398                     if (_pausedGraph) {
56399                         var previous = _pausedGraph;
56400                         _pausedGraph = null;
56401                         return change(previous);
56402                     }
56403                 },
56404
56405
56406                 undoAnnotation: function() {
56407                     var i = _index;
56408                     while (i >= 0) {
56409                         if (_stack[i].annotation) { return _stack[i].annotation; }
56410                         i--;
56411                     }
56412                 },
56413
56414
56415                 redoAnnotation: function() {
56416                     var i = _index + 1;
56417                     while (i <= _stack.length - 1) {
56418                         if (_stack[i].annotation) { return _stack[i].annotation; }
56419                         i++;
56420                     }
56421                 },
56422
56423
56424                 // Returns the entities from the active graph with bounding boxes
56425                 // overlapping the given `extent`.
56426                 intersects: function(extent) {
56427                     return _tree.intersects(extent, _stack[_index].graph);
56428                 },
56429
56430
56431                 difference: function() {
56432                     var base = _stack[0].graph;
56433                     var head = _stack[_index].graph;
56434                     return coreDifference(base, head);
56435                 },
56436
56437
56438                 changes: function(action) {
56439                     var base = _stack[0].graph;
56440                     var head = _stack[_index].graph;
56441
56442                     if (action) {
56443                         head = action(head);
56444                     }
56445
56446                     var difference = coreDifference(base, head);
56447
56448                     return {
56449                         modified: difference.modified(),
56450                         created: difference.created(),
56451                         deleted: difference.deleted()
56452                     };
56453                 },
56454
56455
56456                 hasChanges: function() {
56457                     return this.difference().length() > 0;
56458                 },
56459
56460
56461                 imageryUsed: function(sources) {
56462                     if (sources) {
56463                         _imageryUsed = sources;
56464                         return history;
56465                     } else {
56466                         var s = new Set();
56467                         _stack.slice(1, _index + 1).forEach(function(state) {
56468                             state.imageryUsed.forEach(function(source) {
56469                                 if (source !== 'Custom') {
56470                                     s.add(source);
56471                                 }
56472                             });
56473                         });
56474                         return Array.from(s);
56475                     }
56476                 },
56477
56478
56479                 photoOverlaysUsed: function(sources) {
56480                     if (sources) {
56481                         _photoOverlaysUsed = sources;
56482                         return history;
56483                     } else {
56484                         var s = new Set();
56485                         _stack.slice(1, _index + 1).forEach(function(state) {
56486                             if (state.photoOverlaysUsed && Array.isArray(state.photoOverlaysUsed)) {
56487                                 state.photoOverlaysUsed.forEach(function(photoOverlay) {
56488                                     s.add(photoOverlay);
56489                                 });
56490                             }
56491                         });
56492                         return Array.from(s);
56493                     }
56494                 },
56495
56496
56497                 // save the current history state
56498                 checkpoint: function(key) {
56499                     _checkpoints[key] = {
56500                         stack: _stack,
56501                         index: _index
56502                     };
56503                     return history;
56504                 },
56505
56506
56507                 // restore history state to a given checkpoint or reset completely
56508                 reset: function(key) {
56509                     if (key !== undefined && _checkpoints.hasOwnProperty(key)) {
56510                         _stack = _checkpoints[key].stack;
56511                         _index = _checkpoints[key].index;
56512                     } else {
56513                         _stack = [{graph: coreGraph()}];
56514                         _index = 0;
56515                         _tree = coreTree(_stack[0].graph);
56516                         _checkpoints = {};
56517                     }
56518                     dispatch$1.call('change');
56519                     return history;
56520                 },
56521
56522
56523                 // `toIntroGraph()` is used to export the intro graph used by the walkthrough.
56524                 //
56525                 // To use it:
56526                 //  1. Start the walkthrough.
56527                 //  2. Get to a "free editing" tutorial step
56528                 //  3. Make your edits to the walkthrough map
56529                 //  4. In your browser dev console run:
56530                 //        `id.history().toIntroGraph()`
56531                 //  5. This outputs stringified JSON to the browser console
56532                 //  6. Copy it to `data/intro_graph.json` and prettify it in your code editor
56533                 toIntroGraph: function() {
56534                     var nextID = { n: 0, r: 0, w: 0 };
56535                     var permIDs = {};
56536                     var graph = this.graph();
56537                     var baseEntities = {};
56538
56539                     // clone base entities..
56540                     Object.values(graph.base().entities).forEach(function(entity) {
56541                         var copy = copyIntroEntity(entity);
56542                         baseEntities[copy.id] = copy;
56543                     });
56544
56545                     // replace base entities with head entities..
56546                     Object.keys(graph.entities).forEach(function(id) {
56547                         var entity = graph.entities[id];
56548                         if (entity) {
56549                             var copy = copyIntroEntity(entity);
56550                             baseEntities[copy.id] = copy;
56551                         } else {
56552                             delete baseEntities[id];
56553                         }
56554                     });
56555
56556                     // swap temporary for permanent ids..
56557                     Object.values(baseEntities).forEach(function(entity) {
56558                         if (Array.isArray(entity.nodes)) {
56559                             entity.nodes = entity.nodes.map(function(node) {
56560                                 return permIDs[node] || node;
56561                             });
56562                         }
56563                         if (Array.isArray(entity.members)) {
56564                             entity.members = entity.members.map(function(member) {
56565                                 member.id = permIDs[member.id] || member.id;
56566                                 return member;
56567                             });
56568                         }
56569                     });
56570
56571                     return JSON.stringify({ dataIntroGraph: baseEntities });
56572
56573
56574                     function copyIntroEntity(source) {
56575                         var copy = utilObjectOmit(source, ['type', 'user', 'v', 'version', 'visible']);
56576
56577                         // Note: the copy is no longer an osmEntity, so it might not have `tags`
56578                         if (copy.tags && !Object.keys(copy.tags)) {
56579                             delete copy.tags;
56580                         }
56581
56582                         if (Array.isArray(copy.loc)) {
56583                             copy.loc[0] = +copy.loc[0].toFixed(6);
56584                             copy.loc[1] = +copy.loc[1].toFixed(6);
56585                         }
56586
56587                         var match = source.id.match(/([nrw])-\d*/);  // temporary id
56588                         if (match !== null) {
56589                             var nrw = match[1];
56590                             var permID;
56591                             do { permID = nrw + (++nextID[nrw]); }
56592                             while (baseEntities.hasOwnProperty(permID));
56593
56594                             copy.id = permIDs[source.id] = permID;
56595                         }
56596                         return copy;
56597                     }
56598                 },
56599
56600
56601                 toJSON: function() {
56602                     if (!this.hasChanges()) { return; }
56603
56604                     var allEntities = {};
56605                     var baseEntities = {};
56606                     var base = _stack[0];
56607
56608                     var s = _stack.map(function(i) {
56609                         var modified = [];
56610                         var deleted = [];
56611
56612                         Object.keys(i.graph.entities).forEach(function(id) {
56613                             var entity = i.graph.entities[id];
56614                             if (entity) {
56615                                 var key = osmEntity.key(entity);
56616                                 allEntities[key] = entity;
56617                                 modified.push(key);
56618                             } else {
56619                                 deleted.push(id);
56620                             }
56621
56622                             // make sure that the originals of changed or deleted entities get merged
56623                             // into the base of the _stack after restoring the data from JSON.
56624                             if (id in base.graph.entities) {
56625                                 baseEntities[id] = base.graph.entities[id];
56626                             }
56627                             if (entity && entity.nodes) {
56628                                 // get originals of pre-existing child nodes
56629                                 entity.nodes.forEach(function(nodeID) {
56630                                     if (nodeID in base.graph.entities) {
56631                                         baseEntities[nodeID] = base.graph.entities[nodeID];
56632                                     }
56633                                 });
56634                             }
56635                             // get originals of parent entities too
56636                             var baseParents = base.graph._parentWays[id];
56637                             if (baseParents) {
56638                                 baseParents.forEach(function(parentID) {
56639                                     if (parentID in base.graph.entities) {
56640                                         baseEntities[parentID] = base.graph.entities[parentID];
56641                                     }
56642                                 });
56643                             }
56644                         });
56645
56646                         var x = {};
56647
56648                         if (modified.length) { x.modified = modified; }
56649                         if (deleted.length) { x.deleted = deleted; }
56650                         if (i.imageryUsed) { x.imageryUsed = i.imageryUsed; }
56651                         if (i.photoOverlaysUsed) { x.photoOverlaysUsed = i.photoOverlaysUsed; }
56652                         if (i.annotation) { x.annotation = i.annotation; }
56653                         if (i.transform) { x.transform = i.transform; }
56654                         if (i.selectedIDs) { x.selectedIDs = i.selectedIDs; }
56655
56656                         return x;
56657                     });
56658
56659                     return JSON.stringify({
56660                         version: 3,
56661                         entities: Object.values(allEntities),
56662                         baseEntities: Object.values(baseEntities),
56663                         stack: s,
56664                         nextIDs: osmEntity.id.next,
56665                         index: _index,
56666                         // note the time the changes were saved
56667                         timestamp: (new Date()).getTime()
56668                     });
56669                 },
56670
56671
56672                 fromJSON: function(json, loadChildNodes) {
56673                     var h = JSON.parse(json);
56674                     var loadComplete = true;
56675
56676                     osmEntity.id.next = h.nextIDs;
56677                     _index = h.index;
56678
56679                     if (h.version === 2 || h.version === 3) {
56680                         var allEntities = {};
56681
56682                         h.entities.forEach(function(entity) {
56683                             allEntities[osmEntity.key(entity)] = osmEntity(entity);
56684                         });
56685
56686                         if (h.version === 3) {
56687                             // This merges originals for changed entities into the base of
56688                             // the _stack even if the current _stack doesn't have them (for
56689                             // example when iD has been restarted in a different region)
56690                             var baseEntities = h.baseEntities.map(function(d) { return osmEntity(d); });
56691                             var stack = _stack.map(function(state) { return state.graph; });
56692                             _stack[0].graph.rebase(baseEntities, stack, true);
56693                             _tree.rebase(baseEntities, true);
56694
56695                             // When we restore a modified way, we also need to fetch any missing
56696                             // childnodes that would normally have been downloaded with it.. #2142
56697                             if (loadChildNodes) {
56698                                 var osm = context.connection();
56699                                 var baseWays = baseEntities
56700                                     .filter(function(e) { return e.type === 'way'; });
56701                                 var nodeIDs = baseWays
56702                                     .reduce(function(acc, way) { return utilArrayUnion(acc, way.nodes); }, []);
56703                                 var missing = nodeIDs
56704                                     .filter(function(n) { return !_stack[0].graph.hasEntity(n); });
56705
56706                                 if (missing.length && osm) {
56707                                     loadComplete = false;
56708                                     context.map().redrawEnable(false);
56709
56710                                     var loading = uiLoading(context).blocking(true);
56711                                     context.container().call(loading);
56712
56713                                     var childNodesLoaded = function(err, result) {
56714                                         if (!err) {
56715                                             var visibleGroups = utilArrayGroupBy(result.data, 'visible');
56716                                             var visibles = visibleGroups.true || [];      // alive nodes
56717                                             var invisibles = visibleGroups.false || [];   // deleted nodes
56718
56719                                             if (visibles.length) {
56720                                                 var visibleIDs = visibles.map(function(entity) { return entity.id; });
56721                                                 var stack = _stack.map(function(state) { return state.graph; });
56722                                                 missing = utilArrayDifference(missing, visibleIDs);
56723                                                 _stack[0].graph.rebase(visibles, stack, true);
56724                                                 _tree.rebase(visibles, true);
56725                                             }
56726
56727                                             // fetch older versions of nodes that were deleted..
56728                                             invisibles.forEach(function(entity) {
56729                                                 osm.loadEntityVersion(entity.id, +entity.version - 1, childNodesLoaded);
56730                                             });
56731                                         }
56732
56733                                         if (err || !missing.length) {
56734                                             loading.close();
56735                                             context.map().redrawEnable(true);
56736                                             dispatch$1.call('change');
56737                                             dispatch$1.call('restore', this);
56738                                         }
56739                                     };
56740
56741                                     osm.loadMultiple(missing, childNodesLoaded);
56742                                 }
56743                             }
56744                         }
56745
56746                         _stack = h.stack.map(function(d) {
56747                             var entities = {}, entity;
56748
56749                             if (d.modified) {
56750                                 d.modified.forEach(function(key) {
56751                                     entity = allEntities[key];
56752                                     entities[entity.id] = entity;
56753                                 });
56754                             }
56755
56756                             if (d.deleted) {
56757                                 d.deleted.forEach(function(id) {
56758                                     entities[id] = undefined;
56759                                 });
56760                             }
56761
56762                             return {
56763                                 graph: coreGraph(_stack[0].graph).load(entities),
56764                                 annotation: d.annotation,
56765                                 imageryUsed: d.imageryUsed,
56766                                 photoOverlaysUsed: d.photoOverlaysUsed,
56767                                 transform: d.transform,
56768                                 selectedIDs: d.selectedIDs
56769                             };
56770                         });
56771
56772                     } else { // original version
56773                         _stack = h.stack.map(function(d) {
56774                             var entities = {};
56775
56776                             for (var i in d.entities) {
56777                                 var entity = d.entities[i];
56778                                 entities[i] = entity === 'undefined' ? undefined : osmEntity(entity);
56779                             }
56780
56781                             d.graph = coreGraph(_stack[0].graph).load(entities);
56782                             return d;
56783                         });
56784                     }
56785
56786                     var transform = _stack[_index].transform;
56787                     if (transform) {
56788                         context.map().transformEase(transform, 0);   // 0 = immediate, no easing
56789                     }
56790
56791                     if (loadComplete) {
56792                         dispatch$1.call('change');
56793                         dispatch$1.call('restore', this);
56794                     }
56795
56796                     return history;
56797                 },
56798
56799
56800                 lock: function() {
56801                     return lock.lock();
56802                 },
56803
56804
56805                 unlock: function() {
56806                     lock.unlock();
56807                 },
56808
56809
56810                 save: function() {
56811                     if (lock.locked() &&
56812                         // don't overwrite existing, unresolved changes
56813                         !_hasUnresolvedRestorableChanges) {
56814
56815                         corePreferences(getKey('saved_history'), history.toJSON() || null);
56816                     }
56817                     return history;
56818                 },
56819
56820
56821                 // delete the history version saved in localStorage
56822                 clearSaved: function() {
56823                     context.debouncedSave.cancel();
56824                     if (lock.locked()) {
56825                         _hasUnresolvedRestorableChanges = false;
56826                         corePreferences(getKey('saved_history'), null);
56827
56828                         // clear the changeset metadata associated with the saved history
56829                         corePreferences('comment', null);
56830                         corePreferences('hashtags', null);
56831                         corePreferences('source', null);
56832                     }
56833                     return history;
56834                 },
56835
56836
56837                 savedHistoryJSON: function() {
56838                     return corePreferences(getKey('saved_history'));
56839                 },
56840
56841
56842                 hasRestorableChanges: function() {
56843                     return _hasUnresolvedRestorableChanges;
56844                 },
56845
56846
56847                 // load history from a version stored in localStorage
56848                 restore: function() {
56849                     if (lock.locked()) {
56850                         _hasUnresolvedRestorableChanges = false;
56851                         var json = this.savedHistoryJSON();
56852                         if (json) { history.fromJSON(json, true); }
56853                     }
56854                 },
56855
56856
56857                 _getKey: getKey
56858
56859             };
56860
56861
56862             history.reset();
56863
56864             return utilRebind(history, dispatch$1, 'on');
56865         }
56866
56867         /**
56868          * Look for roads that can be connected to other roads with a short extension
56869          */
56870         function validationAlmostJunction(context) {
56871           var type = 'almost_junction';
56872           var EXTEND_TH_METERS = 5;
56873           var WELD_TH_METERS = 0.75;
56874           // Comes from considering bounding case of parallel ways
56875           var CLOSE_NODE_TH = EXTEND_TH_METERS - WELD_TH_METERS;
56876           // Comes from considering bounding case of perpendicular ways
56877           var SIG_ANGLE_TH = Math.atan(WELD_TH_METERS / EXTEND_TH_METERS);
56878
56879           function isHighway(entity) {
56880             return entity.type === 'way'
56881               && osmRoutableHighwayTagValues[entity.tags.highway];
56882           }
56883
56884           function isTaggedAsNotContinuing(node) {
56885             return node.tags.noexit === 'yes'
56886               || node.tags.amenity === 'parking_entrance'
56887               || (node.tags.entrance && node.tags.entrance !== 'no');
56888           }
56889
56890
56891           var validation = function checkAlmostJunction(entity, graph) {
56892             if (!isHighway(entity)) { return []; }
56893             if (entity.isDegenerate()) { return []; }
56894
56895             var tree = context.history().tree();
56896             var extendableNodeInfos = findConnectableEndNodesByExtension(entity);
56897
56898             var issues = [];
56899
56900             extendableNodeInfos.forEach(function (extendableNodeInfo) {
56901               issues.push(new validationIssue({
56902                 type: type,
56903                 subtype: 'highway-highway',
56904                 severity: 'warning',
56905                 message: function message(context) {
56906                   var entity1 = context.hasEntity(this.entityIds[0]);
56907                   if (this.entityIds[0] === this.entityIds[2]) {
56908                     return entity1 ? _t('issues.almost_junction.self.message', {
56909                       feature: utilDisplayLabel(entity1, context.graph())
56910                     }) : '';
56911                   } else {
56912                     var entity2 = context.hasEntity(this.entityIds[2]);
56913                     return (entity1 && entity2) ? _t('issues.almost_junction.message', {
56914                       feature: utilDisplayLabel(entity1, context.graph()),
56915                       feature2: utilDisplayLabel(entity2, context.graph())
56916                     }) : '';
56917                   }
56918                 },
56919                 reference: showReference,
56920                 entityIds: [
56921                   entity.id,
56922                   extendableNodeInfo.node.id,
56923                   extendableNodeInfo.wid ],
56924                 loc: extendableNodeInfo.node.loc,
56925                 hash: JSON.stringify(extendableNodeInfo.node.loc),
56926                 data: {
56927                   midId: extendableNodeInfo.mid.id,
56928                   edge: extendableNodeInfo.edge,
56929                   cross_loc: extendableNodeInfo.cross_loc
56930                 },
56931                 dynamicFixes: makeFixes
56932               }));
56933             });
56934
56935             return issues;
56936
56937             function makeFixes(context) {
56938               var fixes = [new validationIssueFix({
56939                 icon: 'iD-icon-abutment',
56940                 title: _t('issues.fix.connect_features.title'),
56941                 onClick: function onClick(context) {
56942                   var annotation = _t('issues.fix.connect_almost_junction.annotation');
56943                   var ref = this.issue.entityIds;
56944                   var endNodeId = ref[1];
56945                   var crossWayId = ref[2];
56946                   var midNode = context.entity(this.issue.data.midId);
56947                   var endNode = context.entity(endNodeId);
56948                   var crossWay = context.entity(crossWayId);
56949
56950                   // When endpoints are close, just join if resulting small change in angle (#7201)
56951                   var nearEndNodes = findNearbyEndNodes(endNode, crossWay);
56952                   if (nearEndNodes.length > 0) {
56953                     var collinear = findSmallJoinAngle(midNode, endNode, nearEndNodes);
56954                     if (collinear) {
56955                       context.perform(
56956                         actionMergeNodes([collinear.id, endNode.id], collinear.loc),
56957                         annotation
56958                       );
56959                       return;
56960                     }
56961                   }
56962
56963                   var targetEdge = this.issue.data.edge;
56964                   var crossLoc = this.issue.data.cross_loc;
56965                   var edgeNodes = [context.entity(targetEdge[0]), context.entity(targetEdge[1])];
56966                   var closestNodeInfo = geoSphericalClosestNode(edgeNodes, crossLoc);
56967
56968                   // already a point nearby, just connect to that
56969                   if (closestNodeInfo.distance < WELD_TH_METERS) {
56970                     context.perform(
56971                       actionMergeNodes([closestNodeInfo.node.id, endNode.id], closestNodeInfo.node.loc),
56972                       annotation
56973                     );
56974                   // else add the end node to the edge way
56975                   } else {
56976                     context.perform(
56977                       actionAddMidpoint({loc: crossLoc, edge: targetEdge}, endNode),
56978                       annotation
56979                     );
56980                   }
56981                 }
56982               })];
56983
56984               var node = context.hasEntity(this.entityIds[1]);
56985               if (node && !node.hasInterestingTags()) {
56986                 // node has no descriptive tags, suggest noexit fix
56987                 fixes.push(new validationIssueFix({
56988                   icon: 'maki-barrier',
56989                   title: _t('issues.fix.tag_as_disconnected.title'),
56990                   onClick: function onClick(context) {
56991                     var nodeID = this.issue.entityIds[1];
56992                     var tags = Object.assign({}, context.entity(nodeID).tags);
56993                     tags.noexit = 'yes';
56994                     context.perform(
56995                       actionChangeTags(nodeID, tags),
56996                       _t('issues.fix.tag_as_disconnected.annotation')
56997                     );
56998                   }
56999                 }));
57000               }
57001
57002               return fixes;
57003             }
57004
57005             function showReference(selection) {
57006               selection.selectAll('.issue-reference')
57007                 .data([0])
57008                 .enter()
57009                 .append('div')
57010                 .attr('class', 'issue-reference')
57011                 .text(_t('issues.almost_junction.highway-highway.reference'));
57012             }
57013
57014             function isExtendableCandidate(node, way) {
57015               // can not accurately test vertices on tiles not downloaded from osm - #5938
57016               var osm = services.osm;
57017               if (osm && !osm.isDataLoaded(node.loc)) {
57018                 return false;
57019               }
57020               if (isTaggedAsNotContinuing(node) || graph.parentWays(node).length !== 1) {
57021                 return false;
57022               }
57023
57024               var occurences = 0;
57025               for (var index in way.nodes) {
57026                 if (way.nodes[index] === node.id) {
57027                   occurences += 1;
57028                   if (occurences > 1) {
57029                     return false;
57030                   }
57031                 }
57032               }
57033               return true;
57034             }
57035
57036             function findConnectableEndNodesByExtension(way) {
57037               var results = [];
57038               if (way.isClosed()) { return results; }
57039
57040               var testNodes;
57041               var indices = [0, way.nodes.length - 1];
57042               indices.forEach(function (nodeIndex) {
57043                 var nodeID = way.nodes[nodeIndex];
57044                 var node = graph.entity(nodeID);
57045
57046                 if (!isExtendableCandidate(node, way)) { return; }
57047
57048                 var connectionInfo = canConnectByExtend(way, nodeIndex);
57049                 if (!connectionInfo) { return; }
57050
57051                 testNodes = graph.childNodes(way).slice();   // shallow copy
57052                 testNodes[nodeIndex] = testNodes[nodeIndex].move(connectionInfo.cross_loc);
57053
57054                 // don't flag issue if connecting the ways would cause self-intersection
57055                 if (geoHasSelfIntersections(testNodes, nodeID)) { return; }
57056
57057                 results.push(connectionInfo);
57058               });
57059
57060               return results;
57061             }
57062
57063             function findNearbyEndNodes(node, way) {
57064               return [
57065                 way.nodes[0],
57066                 way.nodes[way.nodes.length - 1]
57067               ].map(function (d) { return graph.entity(d); })
57068               .filter(function (d) {
57069                 // Node cannot be near to itself, but other endnode of same way could be
57070                 return d.id !== node.id
57071                   && geoSphericalDistance(node.loc, d.loc) <= CLOSE_NODE_TH;
57072               });
57073             }
57074
57075             function findSmallJoinAngle(midNode, tipNode, endNodes) {
57076               // Both nodes could be close, so want to join whichever is closest to collinear
57077               var joinTo;
57078               var minAngle = Infinity;
57079
57080               // Checks midNode -> tipNode -> endNode for collinearity
57081               endNodes.forEach(function (endNode) {
57082                 var a1 = geoAngle(midNode, tipNode, context.projection) + Math.PI;
57083                 var a2 = geoAngle(midNode, endNode, context.projection) + Math.PI;
57084                 var diff = Math.max(a1, a2) - Math.min(a1, a2);
57085
57086                 if (diff < minAngle) {
57087                   joinTo = endNode;
57088                   minAngle = diff;
57089                 }
57090               });
57091
57092               /* Threshold set by considering right angle triangle
57093               based on node joining threshold and extension distance */
57094               if (minAngle <= SIG_ANGLE_TH) { return joinTo; }
57095
57096               return null;
57097             }
57098
57099             function hasTag(tags, key) {
57100               return tags[key] !== undefined && tags[key] !== 'no';
57101             }
57102
57103             function canConnectWays(way, way2) {
57104
57105               // allow self-connections
57106               if (way.id === way2.id) { return true; }
57107
57108               // if one is bridge or tunnel, both must be bridge or tunnel
57109               if ((hasTag(way.tags, 'bridge') || hasTag(way2.tags, 'bridge')) &&
57110                 !(hasTag(way.tags, 'bridge') && hasTag(way2.tags, 'bridge'))) { return false; }
57111               if ((hasTag(way.tags, 'tunnel') || hasTag(way2.tags, 'tunnel')) &&
57112                 !(hasTag(way.tags, 'tunnel') && hasTag(way2.tags, 'tunnel'))) { return false; }
57113
57114               // must have equivalent layers and levels
57115               var layer1 = way.tags.layer || '0',
57116                 layer2 = way2.tags.layer || '0';
57117               if (layer1 !== layer2) { return false; }
57118
57119               var level1 = way.tags.level || '0',
57120                 level2 = way2.tags.level || '0';
57121               if (level1 !== level2) { return false; }
57122
57123               return true;
57124             }
57125
57126             function canConnectByExtend(way, endNodeIdx) {
57127               var tipNid = way.nodes[endNodeIdx];  // the 'tip' node for extension point
57128               var midNid = endNodeIdx === 0 ? way.nodes[1] : way.nodes[way.nodes.length - 2];  // the other node of the edge
57129               var tipNode = graph.entity(tipNid);
57130               var midNode = graph.entity(midNid);
57131               var lon = tipNode.loc[0];
57132               var lat = tipNode.loc[1];
57133               var lon_range = geoMetersToLon(EXTEND_TH_METERS, lat) / 2;
57134               var lat_range = geoMetersToLat(EXTEND_TH_METERS) / 2;
57135               var queryExtent = geoExtent([
57136                 [lon - lon_range, lat - lat_range],
57137                 [lon + lon_range, lat + lat_range]
57138               ]);
57139
57140               // first, extend the edge of [midNode -> tipNode] by EXTEND_TH_METERS and find the "extended tip" location
57141               var edgeLen = geoSphericalDistance(midNode.loc, tipNode.loc);
57142               var t = EXTEND_TH_METERS / edgeLen + 1.0;
57143               var extTipLoc = geoVecInterp(midNode.loc, tipNode.loc, t);
57144
57145               // then, check if the extension part [tipNode.loc -> extTipLoc] intersects any other ways
57146               var segmentInfos = tree.waySegments(queryExtent, graph);
57147               for (var i = 0; i < segmentInfos.length; i++) {
57148                 var segmentInfo = segmentInfos[i];
57149
57150                 var way2 = graph.entity(segmentInfo.wayId);
57151
57152                 if (!isHighway(way2)) { continue; }
57153
57154                 if (!canConnectWays(way, way2)) { continue; }
57155
57156                 var nAid = segmentInfo.nodes[0],
57157                   nBid = segmentInfo.nodes[1];
57158
57159                 if (nAid === tipNid || nBid === tipNid) { continue; }
57160
57161                 var nA = graph.entity(nAid),
57162                   nB = graph.entity(nBid);
57163                 var crossLoc = geoLineIntersection([tipNode.loc, extTipLoc], [nA.loc, nB.loc]);
57164                 if (crossLoc) {
57165                   return {
57166                     mid: midNode,
57167                     node: tipNode,
57168                     wid: way2.id,
57169                     edge: [nA.id, nB.id],
57170                     cross_loc: crossLoc
57171                   };
57172                 }
57173               }
57174               return null;
57175             }
57176           };
57177
57178           validation.type = type;
57179
57180           return validation;
57181         }
57182
57183         function validationCloseNodes(context) {
57184             var type = 'close_nodes';
57185
57186             var pointThresholdMeters = 0.2;
57187
57188             var validation = function(entity, graph) {
57189                 if (entity.type === 'node') {
57190                     return getIssuesForNode(entity);
57191                 } else if (entity.type === 'way') {
57192                     return getIssuesForWay(entity);
57193                 }
57194                 return [];
57195
57196                 function getIssuesForNode(node) {
57197                     var parentWays = graph.parentWays(node);
57198                     if (parentWays.length) {
57199                         return getIssuesForVertex(node, parentWays);
57200                     } else {
57201                         return getIssuesForDetachedPoint(node);
57202                     }
57203                 }
57204
57205                 function wayTypeFor(way) {
57206
57207                     if (way.tags.boundary && way.tags.boundary !== 'no') { return 'boundary'; }
57208                     if (way.tags.indoor && way.tags.indoor !== 'no') { return 'indoor'; }
57209                     if ((way.tags.building && way.tags.building !== 'no') ||
57210                         (way.tags['building:part'] && way.tags['building:part'] !== 'no')) { return 'building'; }
57211                     if (osmPathHighwayTagValues[way.tags.highway]) { return 'path'; }
57212
57213                     var parentRelations = graph.parentRelations(way);
57214                     for (var i in parentRelations) {
57215                         var relation = parentRelations[i];
57216
57217                         if (relation.tags.type === 'boundary') { return 'boundary'; }
57218
57219                         if (relation.isMultipolygon()) {
57220                             if (relation.tags.indoor && relation.tags.indoor !== 'no') { return 'indoor'; }
57221                             if ((relation.tags.building && relation.tags.building !== 'no') ||
57222                                 (relation.tags['building:part'] && relation.tags['building:part'] !== 'no')) { return 'building'; }
57223                         }
57224                     }
57225
57226                     return 'other';
57227                 }
57228
57229                 function shouldCheckWay(way) {
57230
57231                     // don't flag issues where merging would create degenerate ways
57232                     if (way.nodes.length <= 2 ||
57233                         (way.isClosed() && way.nodes.length <= 4)) { return false; }
57234
57235                     var bbox = way.extent(graph).bbox();
57236                     var hypotenuseMeters = geoSphericalDistance([bbox.minX, bbox.minY], [bbox.maxX, bbox.maxY]);
57237                     // don't flag close nodes in very small ways
57238                     if (hypotenuseMeters < 1.5) { return false; }
57239
57240                     return true;
57241                 }
57242
57243                 function getIssuesForWay(way) {
57244                     if (!shouldCheckWay(way)) { return []; }
57245
57246                     var issues = [],
57247                         nodes = graph.childNodes(way);
57248                     for (var i = 0; i < nodes.length - 1; i++) {
57249                         var node1 = nodes[i];
57250                         var node2 = nodes[i+1];
57251
57252                         var issue = getWayIssueIfAny(node1, node2, way);
57253                         if (issue) { issues.push(issue); }
57254                     }
57255                     return issues;
57256                 }
57257
57258                 function getIssuesForVertex(node, parentWays) {
57259                     var issues = [];
57260
57261                     function checkForCloseness(node1, node2, way) {
57262                         var issue = getWayIssueIfAny(node1, node2, way);
57263                         if (issue) { issues.push(issue); }
57264                     }
57265
57266                     for (var i = 0; i < parentWays.length; i++) {
57267                         var parentWay = parentWays[i];
57268
57269                         if (!shouldCheckWay(parentWay)) { continue; }
57270
57271                         var lastIndex = parentWay.nodes.length - 1;
57272                         for (var j = 0; j < parentWay.nodes.length; j++) {
57273                             if (j !== 0) {
57274                                 if (parentWay.nodes[j-1] === node.id) {
57275                                     checkForCloseness(node, graph.entity(parentWay.nodes[j]), parentWay);
57276                                 }
57277                             }
57278                             if (j !== lastIndex) {
57279                                 if (parentWay.nodes[j+1] === node.id) {
57280                                     checkForCloseness(graph.entity(parentWay.nodes[j]), node, parentWay);
57281                                 }
57282                             }
57283                         }
57284                     }
57285                     return issues;
57286                 }
57287
57288                 function thresholdMetersForWay(way) {
57289                     if (!shouldCheckWay(way)) { return 0; }
57290
57291                     var wayType = wayTypeFor(way);
57292
57293                     // don't flag boundaries since they might be highly detailed and can't be easily verified
57294                     if (wayType === 'boundary') { return 0; }
57295                     // expect some features to be mapped with higher levels of detail
57296                     if (wayType === 'indoor') { return 0.01; }
57297                     if (wayType === 'building') { return 0.05; }
57298                     if (wayType === 'path') { return 0.1; }
57299                     return 0.2;
57300                 }
57301
57302                 function getIssuesForDetachedPoint(node) {
57303
57304                     var issues = [];
57305
57306                     var lon = node.loc[0];
57307                     var lat = node.loc[1];
57308                     var lon_range = geoMetersToLon(pointThresholdMeters, lat) / 2;
57309                     var lat_range = geoMetersToLat(pointThresholdMeters) / 2;
57310                     var queryExtent = geoExtent([
57311                         [lon - lon_range, lat - lat_range],
57312                         [lon + lon_range, lat + lat_range]
57313                     ]);
57314
57315                     var intersected = context.history().tree().intersects(queryExtent, graph);
57316                     for (var j = 0; j < intersected.length; j++) {
57317                         var nearby = intersected[j];
57318
57319                         if (nearby.id === node.id) { continue; }
57320                         if (nearby.type !== 'node' || nearby.geometry(graph) !== 'point') { continue; }
57321
57322                         if (nearby.loc === node.loc ||
57323                             geoSphericalDistance(node.loc, nearby.loc) < pointThresholdMeters) {
57324
57325                             // allow very close points if tags indicate the z-axis might vary
57326                             var zAxisKeys = { layer: true, level: true, 'addr:housenumber': true, 'addr:unit': true };
57327                             var zAxisDifferentiates = false;
57328                             for (var key in zAxisKeys) {
57329                                 var nodeValue = node.tags[key] || '0';
57330                                 var nearbyValue = nearby.tags[key] || '0';
57331                                 if (nodeValue !== nearbyValue) {
57332                                     zAxisDifferentiates = true;
57333                                     break;
57334                                 }
57335                             }
57336                             if (zAxisDifferentiates) { continue; }
57337
57338                             issues.push(new validationIssue({
57339                                 type: type,
57340                                 subtype: 'detached',
57341                                 severity: 'warning',
57342                                 message: function(context) {
57343                                     var entity = context.hasEntity(this.entityIds[0]),
57344                                         entity2 = context.hasEntity(this.entityIds[1]);
57345                                     return (entity && entity2) ? _t('issues.close_nodes.detached.message', {
57346                                         feature: utilDisplayLabel(entity, context.graph()),
57347                                         feature2: utilDisplayLabel(entity2, context.graph())
57348                                     }) : '';
57349                                 },
57350                                 reference: showReference,
57351                                 entityIds: [node.id, nearby.id],
57352                                 dynamicFixes: function() {
57353                                     return [
57354                                         new validationIssueFix({
57355                                             icon: 'iD-operation-disconnect',
57356                                             title: _t('issues.fix.move_points_apart.title')
57357                                         }),
57358                                         new validationIssueFix({
57359                                             icon: 'iD-icon-layers',
57360                                             title: _t('issues.fix.use_different_layers_or_levels.title')
57361                                         })
57362                                     ];
57363                                 }
57364                             }));
57365                         }
57366                     }
57367
57368                     return issues;
57369
57370                     function showReference(selection) {
57371                         var referenceText = _t('issues.close_nodes.detached.reference');
57372                         selection.selectAll('.issue-reference')
57373                             .data([0])
57374                             .enter()
57375                             .append('div')
57376                             .attr('class', 'issue-reference')
57377                             .text(referenceText);
57378                     }
57379                 }
57380
57381                 function getWayIssueIfAny(node1, node2, way) {
57382                     if (node1.id === node2.id ||
57383                         (node1.hasInterestingTags() && node2.hasInterestingTags())) {
57384                         return null;
57385                     }
57386
57387                     if (node1.loc !== node2.loc) {
57388                         var parentWays1 = graph.parentWays(node1);
57389                         var parentWays2 = new Set(graph.parentWays(node2));
57390
57391                         var sharedWays = parentWays1.filter(function(parentWay) {
57392                             return parentWays2.has(parentWay);
57393                         });
57394
57395                         var thresholds = sharedWays.map(function(parentWay) {
57396                             return thresholdMetersForWay(parentWay);
57397                         });
57398
57399                         var threshold = Math.min.apply(Math, thresholds);
57400                         var distance = geoSphericalDistance(node1.loc, node2.loc);
57401                         if (distance > threshold) { return null; }
57402                     }
57403
57404                     return new validationIssue({
57405                         type: type,
57406                         subtype: 'vertices',
57407                         severity: 'warning',
57408                         message: function(context) {
57409                             var entity = context.hasEntity(this.entityIds[0]);
57410                             return entity ? _t('issues.close_nodes.message', { way: utilDisplayLabel(entity, context.graph()) }) : '';
57411                         },
57412                         reference: showReference,
57413                         entityIds: [way.id, node1.id, node2.id],
57414                         loc: node1.loc,
57415                         dynamicFixes: function() {
57416                             return [
57417                                 new validationIssueFix({
57418                                     icon: 'iD-icon-plus',
57419                                     title: _t('issues.fix.merge_points.title'),
57420                                     onClick: function(context) {
57421                                         var entityIds = this.issue.entityIds;
57422                                         var action = actionMergeNodes([entityIds[1], entityIds[2]]);
57423                                         context.perform(action, _t('issues.fix.merge_close_vertices.annotation'));
57424                                     }
57425                                 }),
57426                                 new validationIssueFix({
57427                                     icon: 'iD-operation-disconnect',
57428                                     title: _t('issues.fix.move_points_apart.title')
57429                                 })
57430                             ];
57431                         }
57432                     });
57433
57434                     function showReference(selection) {
57435                         var referenceText = _t('issues.close_nodes.reference');
57436                         selection.selectAll('.issue-reference')
57437                             .data([0])
57438                             .enter()
57439                             .append('div')
57440                             .attr('class', 'issue-reference')
57441                             .text(referenceText);
57442                     }
57443                 }
57444
57445             };
57446
57447
57448             validation.type = type;
57449
57450             return validation;
57451         }
57452
57453         function validationCrossingWays(context) {
57454             var type = 'crossing_ways';
57455
57456             // returns the way or its parent relation, whichever has a useful feature type
57457             function getFeatureWithFeatureTypeTagsForWay(way, graph) {
57458                 if (getFeatureType(way, graph) === null) {
57459                     // if the way doesn't match a feature type, check its parent relations
57460                     var parentRels = graph.parentRelations(way);
57461                     for (var i = 0; i < parentRels.length; i++) {
57462                         var rel = parentRels[i];
57463                         if (getFeatureType(rel, graph) !== null) {
57464                             return rel;
57465                         }
57466                     }
57467                 }
57468                 return way;
57469             }
57470
57471
57472             function hasTag(tags, key) {
57473                 return tags[key] !== undefined && tags[key] !== 'no';
57474             }
57475
57476             function taggedAsIndoor(tags) {
57477                 return hasTag(tags, 'indoor') ||
57478                     hasTag(tags, 'level') ||
57479                     tags.highway === 'corridor';
57480             }
57481
57482             function allowsBridge(featureType) {
57483                 return featureType === 'highway' || featureType === 'railway' || featureType === 'waterway';
57484             }
57485             function allowsTunnel(featureType) {
57486                 return featureType === 'highway' || featureType === 'railway' || featureType === 'waterway';
57487             }
57488
57489             // discard
57490             var ignoredBuildings = {
57491                 demolished: true, dismantled: true, proposed: true, razed: true
57492             };
57493
57494
57495             function getFeatureType(entity, graph) {
57496
57497                 var geometry = entity.geometry(graph);
57498                 if (geometry !== 'line' && geometry !== 'area') { return null; }
57499
57500                 var tags = entity.tags;
57501
57502                 if (hasTag(tags, 'building') && !ignoredBuildings[tags.building]) { return 'building'; }
57503                 if (hasTag(tags, 'highway') && osmRoutableHighwayTagValues[tags.highway]) { return 'highway'; }
57504
57505                 // don't check railway or waterway areas
57506                 if (geometry !== 'line') { return null; }
57507
57508                 if (hasTag(tags, 'railway') && osmRailwayTrackTagValues[tags.railway]) { return 'railway'; }
57509                 if (hasTag(tags, 'waterway') && osmFlowingWaterwayTagValues[tags.waterway]) { return 'waterway'; }
57510
57511                 return null;
57512             }
57513
57514
57515             function isLegitCrossing(tags1, featureType1, tags2, featureType2) {
57516
57517                 // assume 0 by default
57518                 var level1 = tags1.level || '0';
57519                 var level2 = tags2.level || '0';
57520
57521                 if (taggedAsIndoor(tags1) && taggedAsIndoor(tags2) && level1 !== level2) {
57522                     // assume features don't interact if they're indoor on different levels
57523                     return true;
57524                 }
57525
57526                 // assume 0 by default; don't use way.layer() since we account for structures here
57527                 var layer1 = tags1.layer || '0';
57528                 var layer2 = tags2.layer || '0';
57529
57530                 if (allowsBridge(featureType1) && allowsBridge(featureType2)) {
57531                     if (hasTag(tags1, 'bridge') && !hasTag(tags2, 'bridge')) { return true; }
57532                     if (!hasTag(tags1, 'bridge') && hasTag(tags2, 'bridge')) { return true; }
57533                     // crossing bridges must use different layers
57534                     if (hasTag(tags1, 'bridge') && hasTag(tags2, 'bridge') && layer1 !== layer2) { return true; }
57535                 } else if (allowsBridge(featureType1) && hasTag(tags1, 'bridge')) { return true; }
57536                 else if (allowsBridge(featureType2) && hasTag(tags2, 'bridge')) { return true; }
57537
57538                 if (allowsTunnel(featureType1) && allowsTunnel(featureType2)) {
57539                     if (hasTag(tags1, 'tunnel') && !hasTag(tags2, 'tunnel')) { return true; }
57540                     if (!hasTag(tags1, 'tunnel') && hasTag(tags2, 'tunnel')) { return true; }
57541                     // crossing tunnels must use different layers
57542                     if (hasTag(tags1, 'tunnel') && hasTag(tags2, 'tunnel') && layer1 !== layer2) { return true; }
57543                 } else if (allowsTunnel(featureType1) && hasTag(tags1, 'tunnel')) { return true; }
57544                 else if (allowsTunnel(featureType2) && hasTag(tags2, 'tunnel')) { return true; }
57545
57546                 // don't flag crossing waterways and pier/highways
57547                 if (featureType1 === 'waterway' && featureType2 === 'highway' && tags2.man_made === 'pier') { return true; }
57548                 if (featureType2 === 'waterway' && featureType1 === 'highway' && tags1.man_made === 'pier') { return true; }
57549
57550                 if (featureType1 === 'building' || featureType2 === 'building') {
57551                     // for building crossings, different layers are enough
57552                     if (layer1 !== layer2) { return true; }
57553                 }
57554                 return false;
57555             }
57556
57557
57558             // highway values for which we shouldn't recommend connecting to waterways
57559             var highwaysDisallowingFords = {
57560                 motorway: true, motorway_link: true, trunk: true, trunk_link: true,
57561                 primary: true, primary_link: true, secondary: true, secondary_link: true
57562             };
57563             var nonCrossingHighways = { track: true };
57564
57565             function tagsForConnectionNodeIfAllowed(entity1, entity2, graph) {
57566                 var featureType1 = getFeatureType(entity1, graph);
57567                 var featureType2 = getFeatureType(entity2, graph);
57568
57569                 var geometry1 = entity1.geometry(graph);
57570                 var geometry2 = entity2.geometry(graph);
57571                 var bothLines = geometry1 === 'line' && geometry2 === 'line';
57572
57573                 if (featureType1 === featureType2) {
57574                     if (featureType1 === 'highway') {
57575                         var entity1IsPath = osmPathHighwayTagValues[entity1.tags.highway];
57576                         var entity2IsPath = osmPathHighwayTagValues[entity2.tags.highway];
57577                         if ((entity1IsPath || entity2IsPath) && entity1IsPath !== entity2IsPath) {
57578                             // one feature is a path but not both
57579
57580                             var roadFeature = entity1IsPath ? entity2 : entity1;
57581                             if (nonCrossingHighways[roadFeature.tags.highway]) {
57582                                 // don't mark path connections with certain roads as crossings
57583                                 return {};
57584                             }
57585                             var pathFeature = entity1IsPath ? entity1 : entity2;
57586                             if (['marked', 'unmarked'].indexOf(pathFeature.tags.crossing) !== -1) {
57587                                 // if the path is a crossing, match the crossing type
57588                                 return bothLines ? { highway: 'crossing', crossing: pathFeature.tags.crossing } : {};
57589                             }
57590                             // don't add a `crossing` subtag to ambiguous crossings
57591                             return bothLines ? { highway: 'crossing' } : {};
57592                         }
57593                         return {};
57594                     }
57595                     if (featureType1 === 'waterway') { return {}; }
57596                     if (featureType1 === 'railway') { return {}; }
57597
57598                 } else {
57599                     var featureTypes = [featureType1, featureType2];
57600                     if (featureTypes.indexOf('highway') !== -1) {
57601                         if (featureTypes.indexOf('railway') !== -1) {
57602                             if (!bothLines) { return {}; }
57603
57604                             var isTram = entity1.tags.railway === 'tram' || entity2.tags.railway === 'tram';
57605
57606                             if (osmPathHighwayTagValues[entity1.tags.highway] ||
57607                                 osmPathHighwayTagValues[entity2.tags.highway]) {
57608
57609                                 // path-tram connections use this tag
57610                                 if (isTram) { return { railway: 'tram_crossing' }; }
57611
57612                                 // other path-rail connections use this tag
57613                                 return { railway: 'crossing' };
57614                             } else {
57615                                 // path-tram connections use this tag
57616                                 if (isTram) { return { railway: 'tram_level_crossing' }; }
57617
57618                                 // other road-rail connections use this tag
57619                                 return { railway: 'level_crossing' };
57620                             }
57621                         }
57622
57623                         if (featureTypes.indexOf('waterway') !== -1) {
57624                             // do not allow fords on structures
57625                             if (hasTag(entity1.tags, 'tunnel') && hasTag(entity2.tags, 'tunnel')) { return null; }
57626                             if (hasTag(entity1.tags, 'bridge') && hasTag(entity2.tags, 'bridge')) { return null; }
57627
57628                             if (highwaysDisallowingFords[entity1.tags.highway] ||
57629                                 highwaysDisallowingFords[entity2.tags.highway]) {
57630                                 // do not allow fords on major highways
57631                                 return null;
57632                             }
57633                             return bothLines ? { ford: 'yes' } : {};
57634                         }
57635                     }
57636                 }
57637                 return null;
57638             }
57639
57640
57641             function findCrossingsByWay(way1, graph, tree) {
57642                 var edgeCrossInfos = [];
57643                 if (way1.type !== 'way') { return edgeCrossInfos; }
57644
57645                 var taggedFeature1 = getFeatureWithFeatureTypeTagsForWay(way1, graph);
57646                 var way1FeatureType = getFeatureType(taggedFeature1, graph);
57647                 if (way1FeatureType === null) { return edgeCrossInfos; }
57648
57649                 var checkedSingleCrossingWays = {};
57650
57651                 // declare vars ahead of time to reduce garbage collection
57652                 var i, j;
57653                 var extent;
57654                 var n1, n2, nA, nB, nAId, nBId;
57655                 var segment1, segment2;
57656                 var oneOnly;
57657                 var segmentInfos, segment2Info, way2, taggedFeature2, way2FeatureType;
57658                 var way1Nodes = graph.childNodes(way1);
57659                 var comparedWays = {};
57660                 for (i = 0; i < way1Nodes.length - 1; i++) {
57661                     n1 = way1Nodes[i];
57662                     n2 = way1Nodes[i + 1];
57663                     extent = geoExtent([
57664                         [
57665                             Math.min(n1.loc[0], n2.loc[0]),
57666                             Math.min(n1.loc[1], n2.loc[1])
57667                         ],
57668                         [
57669                             Math.max(n1.loc[0], n2.loc[0]),
57670                             Math.max(n1.loc[1], n2.loc[1])
57671                         ]
57672                     ]);
57673
57674                     // Optimize by only checking overlapping segments, not every segment
57675                     // of overlapping ways
57676                     segmentInfos = tree.waySegments(extent, graph);
57677
57678                     for (j = 0; j < segmentInfos.length; j++) {
57679                         segment2Info = segmentInfos[j];
57680
57681                         // don't check for self-intersection in this validation
57682                         if (segment2Info.wayId === way1.id) { continue; }
57683
57684                         // skip if this way was already checked and only one issue is needed
57685                         if (checkedSingleCrossingWays[segment2Info.wayId]) { continue; }
57686
57687                         // mark this way as checked even if there are no crossings
57688                         comparedWays[segment2Info.wayId] = true;
57689
57690                         way2 = graph.hasEntity(segment2Info.wayId);
57691                         if (!way2) { continue; }
57692                         taggedFeature2 = getFeatureWithFeatureTypeTagsForWay(way2, graph);
57693                         // only check crossing highway, waterway, building, and railway
57694                         way2FeatureType = getFeatureType(taggedFeature2, graph);
57695
57696                         if (way2FeatureType === null ||
57697                             isLegitCrossing(taggedFeature1.tags, way1FeatureType, taggedFeature2.tags, way2FeatureType)) {
57698                             continue;
57699                         }
57700
57701                         // create only one issue for building crossings
57702                         oneOnly = way1FeatureType === 'building' || way2FeatureType === 'building';
57703
57704                         nAId = segment2Info.nodes[0];
57705                         nBId = segment2Info.nodes[1];
57706                         if (nAId === n1.id || nAId === n2.id ||
57707                             nBId === n1.id || nBId === n2.id) {
57708                             // n1 or n2 is a connection node; skip
57709                             continue;
57710                         }
57711                         nA = graph.hasEntity(nAId);
57712                         if (!nA) { continue; }
57713                         nB = graph.hasEntity(nBId);
57714                         if (!nB) { continue; }
57715
57716                         segment1 = [n1.loc, n2.loc];
57717                         segment2 = [nA.loc, nB.loc];
57718                         var point = geoLineIntersection(segment1, segment2);
57719                         if (point) {
57720                             edgeCrossInfos.push({
57721                                 wayInfos: [
57722                                     {
57723                                         way: way1,
57724                                         featureType: way1FeatureType,
57725                                         edge: [n1.id, n2.id]
57726                                     },
57727                                     {
57728                                         way: way2,
57729                                         featureType: way2FeatureType,
57730                                         edge: [nA.id, nB.id]
57731                                     }
57732                                 ],
57733                                 crossPoint: point
57734                             });
57735                             if (oneOnly) {
57736                                 checkedSingleCrossingWays[way2.id] = true;
57737                                 break;
57738                             }
57739                         }
57740                     }
57741                 }
57742                 return edgeCrossInfos;
57743             }
57744
57745
57746             function waysToCheck(entity, graph) {
57747                 var featureType = getFeatureType(entity, graph);
57748                 if (!featureType) { return []; }
57749
57750                 if (entity.type === 'way') {
57751                     return [entity];
57752                 } else if (entity.type === 'relation') {
57753                     return entity.members.reduce(function(array, member) {
57754                         if (member.type === 'way' &&
57755                             // only look at geometry ways
57756                             (!member.role || member.role === 'outer' || member.role === 'inner')) {
57757                             var entity = graph.hasEntity(member.id);
57758                             // don't add duplicates
57759                             if (entity && array.indexOf(entity) === -1) {
57760                                 array.push(entity);
57761                             }
57762                         }
57763                         return array;
57764                     }, []);
57765                 }
57766                 return [];
57767             }
57768
57769
57770             var validation = function checkCrossingWays(entity, graph) {
57771
57772                 var tree = context.history().tree();
57773
57774                 var ways = waysToCheck(entity, graph);
57775
57776                 var issues = [];
57777                 // declare these here to reduce garbage collection
57778                 var wayIndex, crossingIndex, crossings;
57779                 for (wayIndex in ways) {
57780                     crossings = findCrossingsByWay(ways[wayIndex], graph, tree);
57781                     for (crossingIndex in crossings) {
57782                         issues.push(createIssue(crossings[crossingIndex], graph));
57783                     }
57784                 }
57785                 return issues;
57786             };
57787
57788
57789             function createIssue(crossing, graph) {
57790
57791                 // use the entities with the tags that define the feature type
57792                 crossing.wayInfos.sort(function(way1Info, way2Info) {
57793                     var type1 = way1Info.featureType;
57794                     var type2 = way2Info.featureType;
57795                     if (type1 === type2) {
57796                         return utilDisplayLabel(way1Info.way, graph) > utilDisplayLabel(way2Info.way, graph);
57797                     } else if (type1 === 'waterway') {
57798                         return true;
57799                     } else if (type2 === 'waterway') {
57800                         return false;
57801                     }
57802                     return type1 < type2;
57803                 });
57804                 var entities = crossing.wayInfos.map(function(wayInfo) {
57805                     return getFeatureWithFeatureTypeTagsForWay(wayInfo.way, graph);
57806                 });
57807                 var edges = [crossing.wayInfos[0].edge, crossing.wayInfos[1].edge];
57808                 var featureTypes = [crossing.wayInfos[0].featureType, crossing.wayInfos[1].featureType];
57809
57810                 var connectionTags = tagsForConnectionNodeIfAllowed(entities[0], entities[1], graph);
57811
57812                 var featureType1 = crossing.wayInfos[0].featureType;
57813                 var featureType2 = crossing.wayInfos[1].featureType;
57814
57815                 var isCrossingIndoors = taggedAsIndoor(entities[0].tags) && taggedAsIndoor(entities[1].tags);
57816                 var isCrossingTunnels = allowsTunnel(featureType1) && hasTag(entities[0].tags, 'tunnel') &&
57817                                         allowsTunnel(featureType2) && hasTag(entities[1].tags, 'tunnel');
57818                 var isCrossingBridges = allowsBridge(featureType1) && hasTag(entities[0].tags, 'bridge') &&
57819                                         allowsBridge(featureType2) && hasTag(entities[1].tags, 'bridge');
57820
57821                 var subtype = [featureType1, featureType2].sort().join('-');
57822
57823                 var crossingTypeID = subtype;
57824
57825                 if (isCrossingIndoors) {
57826                     crossingTypeID = 'indoor-indoor';
57827                 } else if (isCrossingTunnels) {
57828                     crossingTypeID = 'tunnel-tunnel';
57829                 } else if (isCrossingBridges) {
57830                     crossingTypeID = 'bridge-bridge';
57831                 }
57832                 if (connectionTags && (isCrossingIndoors || isCrossingTunnels || isCrossingBridges)) {
57833                     crossingTypeID += '_connectable';
57834                 }
57835
57836                 return new validationIssue({
57837                     type: type,
57838                     subtype: subtype,
57839                     severity: 'warning',
57840                     message: function(context) {
57841                         var graph = context.graph();
57842                         var entity1 = graph.hasEntity(this.entityIds[0]),
57843                             entity2 = graph.hasEntity(this.entityIds[1]);
57844                         return (entity1 && entity2) ? _t('issues.crossing_ways.message', {
57845                             feature: utilDisplayLabel(entity1, graph),
57846                             feature2: utilDisplayLabel(entity2, graph)
57847                         }) : '';
57848                     },
57849                     reference: showReference,
57850                     entityIds: entities.map(function(entity) {
57851                         return entity.id;
57852                     }),
57853                     data: {
57854                         edges: edges,
57855                         featureTypes: featureTypes,
57856                         connectionTags: connectionTags
57857                     },
57858                     // differentiate based on the loc since two ways can cross multiple times
57859                     hash: crossing.crossPoint.toString() +
57860                         // if the edges change then so does the fix
57861                         edges.slice().sort(function(edge1, edge2) {
57862                             // order to assure hash is deterministic
57863                             return edge1[0] < edge2[0] ? -1 : 1;
57864                         }).toString() +
57865                         // ensure the correct connection tags are added in the fix
57866                         JSON.stringify(connectionTags),
57867                     loc: crossing.crossPoint,
57868                     dynamicFixes: function(context) {
57869                         var mode = context.mode();
57870                         if (!mode || mode.id !== 'select' || mode.selectedIDs().length !== 1) { return []; }
57871
57872                         var selectedIndex = this.entityIds[0] === mode.selectedIDs()[0] ? 0 : 1;
57873                         var selectedFeatureType = this.data.featureTypes[selectedIndex];
57874                         var otherFeatureType = this.data.featureTypes[selectedIndex === 0 ? 1 : 0];
57875
57876                         var fixes = [];
57877
57878                         if (connectionTags) {
57879                             fixes.push(makeConnectWaysFix(this.data.connectionTags));
57880                         }
57881
57882                         if (isCrossingIndoors) {
57883                             fixes.push(new validationIssueFix({
57884                                 icon: 'iD-icon-layers',
57885                                 title: _t('issues.fix.use_different_levels.title')
57886                             }));
57887                         } else if (isCrossingTunnels ||
57888                             isCrossingBridges ||
57889                             featureType1 === 'building' ||
57890                             featureType2 === 'building')  {
57891
57892                             fixes.push(makeChangeLayerFix('higher'));
57893                             fixes.push(makeChangeLayerFix('lower'));
57894
57895                         // can only add bridge/tunnel if both features are lines
57896                         } else if (context.graph().geometry(this.entityIds[0]) === 'line' &&
57897                             context.graph().geometry(this.entityIds[1]) === 'line') {
57898
57899                             // don't recommend adding bridges to waterways since they're uncommon
57900                             if (allowsBridge(selectedFeatureType) && selectedFeatureType !== 'waterway') {
57901                                 fixes.push(makeAddBridgeOrTunnelFix('add_a_bridge', 'temaki-bridge', 'bridge'));
57902                             }
57903
57904                             // don't recommend adding tunnels under waterways since they're uncommon
57905                             var skipTunnelFix = otherFeatureType === 'waterway' && selectedFeatureType !== 'waterway';
57906                             if (allowsTunnel(selectedFeatureType) && !skipTunnelFix) {
57907                                 fixes.push(makeAddBridgeOrTunnelFix('add_a_tunnel', 'temaki-tunnel', 'tunnel'));
57908                             }
57909                         }
57910
57911                         // repositioning the features is always an option
57912                         fixes.push(new validationIssueFix({
57913                             icon: 'iD-operation-move',
57914                             title: _t('issues.fix.reposition_features.title')
57915                         }));
57916
57917                         return fixes;
57918                     }
57919                 });
57920
57921                 function showReference(selection) {
57922                     selection.selectAll('.issue-reference')
57923                         .data([0])
57924                         .enter()
57925                         .append('div')
57926                         .attr('class', 'issue-reference')
57927                         .text(_t('issues.crossing_ways.' + crossingTypeID + '.reference'));
57928                 }
57929             }
57930
57931             function makeAddBridgeOrTunnelFix(fixTitleID, iconName, bridgeOrTunnel){
57932                 return new validationIssueFix({
57933                     icon: iconName,
57934                     title: _t('issues.fix.' + fixTitleID + '.title'),
57935                     onClick: function(context) {
57936                         var mode = context.mode();
57937                         if (!mode || mode.id !== 'select') { return; }
57938
57939                         var selectedIDs = mode.selectedIDs();
57940                         if (selectedIDs.length !== 1) { return; }
57941
57942                         var selectedWayID = selectedIDs[0];
57943                         if (!context.hasEntity(selectedWayID)) { return; }
57944
57945                         var resultWayIDs = [selectedWayID];
57946
57947                         var edge, crossedEdge, crossedWayID;
57948                         if (this.issue.entityIds[0] === selectedWayID) {
57949                             edge = this.issue.data.edges[0];
57950                             crossedEdge = this.issue.data.edges[1];
57951                             crossedWayID = this.issue.entityIds[1];
57952                         } else {
57953                             edge = this.issue.data.edges[1];
57954                             crossedEdge = this.issue.data.edges[0];
57955                             crossedWayID = this.issue.entityIds[0];
57956                         }
57957
57958                         var crossingLoc = this.issue.loc;
57959
57960                         var projection = context.projection;
57961
57962                         var action = function actionAddStructure(graph) {
57963
57964                             var edgeNodes = [graph.entity(edge[0]), graph.entity(edge[1])];
57965
57966                             var crossedWay = graph.hasEntity(crossedWayID);
57967                             // use the explicit width of the crossed feature as the structure length, if available
57968                             var structLengthMeters = crossedWay && crossedWay.tags.width && parseFloat(crossedWay.tags.width);
57969                             if (!structLengthMeters) {
57970                                 // if no explicit width is set, approximate the width based on the tags
57971                                 structLengthMeters = crossedWay && crossedWay.impliedLineWidthMeters();
57972                             }
57973                             if (structLengthMeters) {
57974                                 if (getFeatureType(crossedWay, graph) === 'railway') {
57975                                     // bridges over railways are generally much longer than the rail bed itself, compensate
57976                                     structLengthMeters *= 2;
57977                                 }
57978                             } else {
57979                                 // should ideally never land here since all rail/water/road tags should have an implied width
57980                                 structLengthMeters = 8;
57981                             }
57982
57983                             var a1 = geoAngle(edgeNodes[0], edgeNodes[1], projection) + Math.PI;
57984                             var a2 = geoAngle(graph.entity(crossedEdge[0]), graph.entity(crossedEdge[1]), projection) + Math.PI;
57985                             var crossingAngle = Math.max(a1, a2) - Math.min(a1, a2);
57986                             if (crossingAngle > Math.PI) { crossingAngle -= Math.PI; }
57987                             // lengthen the structure to account for the angle of the crossing
57988                             structLengthMeters = ((structLengthMeters / 2) / Math.sin(crossingAngle)) * 2;
57989
57990                             // add padding since the structure must extend past the edges of the crossed feature
57991                             structLengthMeters += 4;
57992
57993                             // clamp the length to a reasonable range
57994                             structLengthMeters = Math.min(Math.max(structLengthMeters, 4), 50);
57995
57996                             function geomToProj(geoPoint) {
57997                                 return [
57998                                     geoLonToMeters(geoPoint[0], geoPoint[1]),
57999                                     geoLatToMeters(geoPoint[1])
58000                                 ];
58001                             }
58002                             function projToGeom(projPoint) {
58003                                 var lat = geoMetersToLat(projPoint[1]);
58004                                 return [
58005                                     geoMetersToLon(projPoint[0], lat),
58006                                     lat
58007                                 ];
58008                             }
58009
58010                             var projEdgeNode1 = geomToProj(edgeNodes[0].loc);
58011                             var projEdgeNode2 = geomToProj(edgeNodes[1].loc);
58012
58013                             var projectedAngle = geoVecAngle(projEdgeNode1, projEdgeNode2);
58014
58015                             var projectedCrossingLoc = geomToProj(crossingLoc);
58016                             var linearToSphericalMetersRatio = geoVecLength(projEdgeNode1, projEdgeNode2) /
58017                                 geoSphericalDistance(edgeNodes[0].loc, edgeNodes[1].loc);
58018
58019                             function locSphericalDistanceFromCrossingLoc(angle, distanceMeters) {
58020                                 var lengthSphericalMeters = distanceMeters * linearToSphericalMetersRatio;
58021                                 return projToGeom([
58022                                     projectedCrossingLoc[0] + Math.cos(angle) * lengthSphericalMeters,
58023                                     projectedCrossingLoc[1] + Math.sin(angle) * lengthSphericalMeters
58024                                 ]);
58025                             }
58026
58027                             var endpointLocGetter1 = function(lengthMeters) {
58028                                 return locSphericalDistanceFromCrossingLoc(projectedAngle, lengthMeters);
58029                             };
58030                             var endpointLocGetter2 = function(lengthMeters) {
58031                                 return locSphericalDistanceFromCrossingLoc(projectedAngle + Math.PI, lengthMeters);
58032                             };
58033
58034                             // avoid creating very short edges from splitting too close to another node
58035                             var minEdgeLengthMeters = 0.55;
58036
58037                             // decide where to bound the structure along the way, splitting as necessary
58038                             function determineEndpoint(edge, endNode, locGetter) {
58039                                 var newNode;
58040
58041                                 var idealLengthMeters = structLengthMeters / 2;
58042
58043                                 // distance between the crossing location and the end of the edge,
58044                                 // the maximum length of this side of the structure
58045                                 var crossingToEdgeEndDistance = geoSphericalDistance(crossingLoc, endNode.loc);
58046
58047                                 if (crossingToEdgeEndDistance - idealLengthMeters > minEdgeLengthMeters) {
58048                                     // the edge is long enough to insert a new node
58049
58050                                     // the loc that would result in the full expected length
58051                                     var idealNodeLoc = locGetter(idealLengthMeters);
58052
58053                                     newNode = osmNode();
58054                                     graph = actionAddMidpoint({ loc: idealNodeLoc, edge: edge }, newNode)(graph);
58055
58056                                 } else {
58057                                     var edgeCount = 0;
58058                                     endNode.parentIntersectionWays(graph).forEach(function(way) {
58059                                         way.nodes.forEach(function(nodeID) {
58060                                             if (nodeID === endNode.id) {
58061                                                 if ((endNode.id === way.first() && endNode.id !== way.last()) ||
58062                                                     (endNode.id === way.last() && endNode.id !== way.first())) {
58063                                                     edgeCount += 1;
58064                                                 } else {
58065                                                     edgeCount += 2;
58066                                                 }
58067                                             }
58068                                         });
58069                                     });
58070
58071                                     if (edgeCount >= 3) {
58072                                         // the end node is a junction, try to leave a segment
58073                                         // between it and the structure - #7202
58074
58075                                         var insetLength = crossingToEdgeEndDistance - minEdgeLengthMeters;
58076                                         if (insetLength > minEdgeLengthMeters) {
58077                                             var insetNodeLoc = locGetter(insetLength);
58078                                             newNode = osmNode();
58079                                             graph = actionAddMidpoint({ loc: insetNodeLoc, edge: edge }, newNode)(graph);
58080                                         }
58081                                     }
58082                                 }
58083
58084                                 // if the edge is too short to subdivide as desired, then
58085                                 // just bound the structure at the existing end node
58086                                 if (!newNode) { newNode = endNode; }
58087
58088                                 var splitAction = actionSplit(newNode.id)
58089                                     .limitWays(resultWayIDs); // only split selected or created ways
58090
58091                                 // do the split
58092                                 graph = splitAction(graph);
58093                                 if (splitAction.getCreatedWayIDs().length) {
58094                                     resultWayIDs.push(splitAction.getCreatedWayIDs()[0]);
58095                                 }
58096
58097                                 return newNode;
58098                             }
58099
58100                             var structEndNode1 = determineEndpoint(edge, edgeNodes[1], endpointLocGetter1);
58101                             var structEndNode2 = determineEndpoint([edgeNodes[0].id, structEndNode1.id], edgeNodes[0], endpointLocGetter2);
58102
58103                             var structureWay = resultWayIDs.map(function(id) {
58104                                 return graph.entity(id);
58105                             }).find(function(way) {
58106                                 return way.nodes.indexOf(structEndNode1.id) !== -1 &&
58107                                     way.nodes.indexOf(structEndNode2.id) !== -1;
58108                             });
58109
58110                             var tags = Object.assign({}, structureWay.tags); // copy tags
58111                             if (bridgeOrTunnel === 'bridge'){
58112                                 tags.bridge = 'yes';
58113                                 tags.layer = '1';
58114                             } else {
58115                                 var tunnelValue = 'yes';
58116                                 if (getFeatureType(structureWay, graph) === 'waterway') {
58117                                     // use `tunnel=culvert` for waterways by default
58118                                     tunnelValue = 'culvert';
58119                                 }
58120                                 tags.tunnel = tunnelValue;
58121                                 tags.layer = '-1';
58122                             }
58123                             // apply the structure tags to the way
58124                             graph = actionChangeTags(structureWay.id, tags)(graph);
58125                             return graph;
58126                         };
58127
58128                         context.perform(action, _t('issues.fix.' + fixTitleID + '.annotation'));
58129                         context.enter(modeSelect(context, resultWayIDs));
58130                     }
58131                 });
58132             }
58133
58134             function makeConnectWaysFix(connectionTags) {
58135
58136                 var fixTitleID = 'connect_features';
58137                 if (connectionTags.ford) {
58138                     fixTitleID = 'connect_using_ford';
58139                 }
58140
58141                 return new validationIssueFix({
58142                     icon: 'iD-icon-crossing',
58143                     title: _t('issues.fix.' + fixTitleID + '.title'),
58144                     onClick: function(context) {
58145                         var loc = this.issue.loc;
58146                         var connectionTags = this.issue.data.connectionTags;
58147                         var edges = this.issue.data.edges;
58148
58149                         context.perform(
58150                             function actionConnectCrossingWays(graph) {
58151                                 // create the new node for the points
58152                                 var node = osmNode({ loc: loc, tags: connectionTags });
58153                                 graph = graph.replace(node);
58154
58155                                 var nodesToMerge = [node.id];
58156                                 var mergeThresholdInMeters = 0.75;
58157
58158                                 edges.forEach(function(edge) {
58159                                     var edgeNodes = [graph.entity(edge[0]), graph.entity(edge[1])];
58160                                     var closestNodeInfo = geoSphericalClosestNode(edgeNodes, loc);
58161                                     // if there is already a point nearby, use that
58162                                     if (closestNodeInfo.distance < mergeThresholdInMeters) {
58163                                         nodesToMerge.push(closestNodeInfo.node.id);
58164                                     // else add the new node to the way
58165                                     } else {
58166                                         graph = actionAddMidpoint({loc: loc, edge: edge}, node)(graph);
58167                                     }
58168                                 });
58169
58170                                 if (nodesToMerge.length > 1) {
58171                                     // if we're using nearby nodes, merge them with the new node
58172                                     graph = actionMergeNodes(nodesToMerge, loc)(graph);
58173                                 }
58174
58175                                 return graph;
58176                             },
58177                             _t('issues.fix.connect_crossing_features.annotation')
58178                         );
58179                     }
58180                 });
58181             }
58182
58183             function makeChangeLayerFix(higherOrLower) {
58184                 return new validationIssueFix({
58185                     icon: 'iD-icon-' + (higherOrLower === 'higher' ? 'up' : 'down'),
58186                     title: _t('issues.fix.tag_this_as_' + higherOrLower + '.title'),
58187                     onClick: function(context) {
58188
58189                         var mode = context.mode();
58190                         if (!mode || mode.id !== 'select') { return; }
58191
58192                         var selectedIDs = mode.selectedIDs();
58193                         if (selectedIDs.length !== 1) { return; }
58194
58195                         var selectedID = selectedIDs[0];
58196                         if (!this.issue.entityIds.some(function(entityId) {
58197                             return entityId === selectedID;
58198                         })) { return; }
58199
58200                         var entity = context.hasEntity(selectedID);
58201                         if (!entity) { return; }
58202
58203                         var tags = Object.assign({}, entity.tags);   // shallow copy
58204                         var layer = tags.layer && Number(tags.layer);
58205                         if (layer && !isNaN(layer)) {
58206                             if (higherOrLower === 'higher') {
58207                                 layer += 1;
58208                             } else {
58209                                 layer -= 1;
58210                             }
58211                         } else {
58212                             if (higherOrLower === 'higher') {
58213                                 layer = 1;
58214                             } else {
58215                                 layer = -1;
58216                             }
58217                         }
58218                         tags.layer = layer.toString();
58219                         context.perform(
58220                             actionChangeTags(entity.id, tags),
58221                             _t('operations.change_tags.annotation')
58222                         );
58223                     }
58224                 });
58225             }
58226
58227             validation.type = type;
58228
58229             return validation;
58230         }
58231
58232         function validationDisconnectedWay() {
58233             var type = 'disconnected_way';
58234
58235             function isTaggedAsHighway(entity) {
58236                 return osmRoutableHighwayTagValues[entity.tags.highway];
58237             }
58238
58239             var validation = function checkDisconnectedWay(entity, graph) {
58240
58241                 var routingIslandWays = routingIslandForEntity(entity);
58242                 if (!routingIslandWays) { return []; }
58243
58244                 return [new validationIssue({
58245                     type: type,
58246                     subtype: 'highway',
58247                     severity: 'warning',
58248                     message: function(context) {
58249                         if (this.entityIds.length === 1) {
58250                             var entity = context.hasEntity(this.entityIds[0]);
58251                             return entity ? _t('issues.disconnected_way.highway.message', { highway: utilDisplayLabel(entity, context.graph()) }) : '';
58252                         }
58253                         return _t('issues.disconnected_way.routable.message.multiple', { count: this.entityIds.length.toString() });
58254                     },
58255                     reference: showReference,
58256                     entityIds: Array.from(routingIslandWays).map(function(way) { return way.id; }),
58257                     dynamicFixes: makeFixes
58258                 })];
58259
58260
58261                 function makeFixes(context) {
58262
58263                     var fixes = [];
58264
58265                     var singleEntity = this.entityIds.length === 1 && context.hasEntity(this.entityIds[0]);
58266
58267                     if (singleEntity) {
58268
58269                         if (singleEntity.type === 'way' && !singleEntity.isClosed()) {
58270
58271                             var textDirection = _mainLocalizer.textDirection();
58272
58273                             var startFix = makeContinueDrawingFixIfAllowed(textDirection, singleEntity.first(), 'start');
58274                             if (startFix) { fixes.push(startFix); }
58275
58276                             var endFix = makeContinueDrawingFixIfAllowed(textDirection, singleEntity.last(), 'end');
58277                             if (endFix) { fixes.push(endFix); }
58278                         }
58279                         if (!fixes.length) {
58280                             fixes.push(new validationIssueFix({
58281                                 title: _t('issues.fix.connect_feature.title')
58282                             }));
58283                         }
58284
58285                         fixes.push(new validationIssueFix({
58286                             icon: 'iD-operation-delete',
58287                             title: _t('issues.fix.delete_feature.title'),
58288                             entityIds: [singleEntity.id],
58289                             onClick: function(context) {
58290                                 var id = this.issue.entityIds[0];
58291                                 var operation = operationDelete(context, [id]);
58292                                 if (!operation.disabled()) {
58293                                     operation();
58294                                 }
58295                             }
58296                         }));
58297                     } else {
58298                         fixes.push(new validationIssueFix({
58299                             title: _t('issues.fix.connect_features.title')
58300                         }));
58301                     }
58302
58303                     return fixes;
58304                 }
58305
58306
58307                 function showReference(selection) {
58308                     selection.selectAll('.issue-reference')
58309                         .data([0])
58310                         .enter()
58311                         .append('div')
58312                         .attr('class', 'issue-reference')
58313                         .text(_t('issues.disconnected_way.routable.reference'));
58314                 }
58315
58316                 function routingIslandForEntity(entity) {
58317
58318                     var routingIsland = new Set();  // the interconnected routable features
58319                     var waysToCheck = [];           // the queue of remaining routable ways to traverse
58320
58321                     function queueParentWays(node) {
58322                         graph.parentWays(node).forEach(function(parentWay) {
58323                             if (!routingIsland.has(parentWay) &&    // only check each feature once
58324                                 isRoutableWay(parentWay, false)) {  // only check routable features
58325                                 routingIsland.add(parentWay);
58326                                 waysToCheck.push(parentWay);
58327                             }
58328                         });
58329                     }
58330
58331                     if (entity.type === 'way' && isRoutableWay(entity, true)) {
58332
58333                         routingIsland.add(entity);
58334                         waysToCheck.push(entity);
58335
58336                     } else if (entity.type === 'node' && isRoutableNode(entity)) {
58337
58338                         routingIsland.add(entity);
58339                         queueParentWays(entity);
58340
58341                     } else {
58342                         // this feature isn't routable, cannot be a routing island
58343                         return null;
58344                     }
58345
58346                     while (waysToCheck.length) {
58347                         var wayToCheck = waysToCheck.pop();
58348                         var childNodes = graph.childNodes(wayToCheck);
58349                         for (var i in childNodes) {
58350                             var vertex = childNodes[i];
58351
58352                             if (isConnectedVertex(vertex)) {
58353                                 // found a link to the wider network, not a routing island
58354                                 return null;
58355                             }
58356
58357                             if (isRoutableNode(vertex)) {
58358                                 routingIsland.add(vertex);
58359                             }
58360
58361                             queueParentWays(vertex);
58362                         }
58363                     }
58364
58365                     // no network link found, this is a routing island, return its members
58366                     return routingIsland;
58367                 }
58368
58369                 function isConnectedVertex(vertex) {
58370                     // assume ways overlapping unloaded tiles are connected to the wider road network  - #5938
58371                     var osm = services.osm;
58372                     if (osm && !osm.isDataLoaded(vertex.loc)) { return true; }
58373
58374                     // entrances are considered connected
58375                     if (vertex.tags.entrance &&
58376                         vertex.tags.entrance !== 'no') { return true; }
58377                     if (vertex.tags.amenity === 'parking_entrance') { return true; }
58378
58379                     return false;
58380                 }
58381
58382                 function isRoutableNode(node) {
58383                     // treat elevators as distinct features in the highway network
58384                     if (node.tags.highway === 'elevator') { return true; }
58385                     return false;
58386                 }
58387
58388                 function isRoutableWay(way, ignoreInnerWays) {
58389                     if (isTaggedAsHighway(way) || way.tags.route === 'ferry') { return true; }
58390
58391                     return graph.parentRelations(way).some(function(parentRelation) {
58392                         if (parentRelation.tags.type === 'route' &&
58393                             parentRelation.tags.route === 'ferry') { return true; }
58394
58395                         if (parentRelation.isMultipolygon() &&
58396                             isTaggedAsHighway(parentRelation) &&
58397                             (!ignoreInnerWays || parentRelation.memberById(way.id).role !== 'inner')) { return true; }
58398                     });
58399                 }
58400
58401                 function makeContinueDrawingFixIfAllowed(textDirection, vertexID, whichEnd) {
58402                     var vertex = graph.hasEntity(vertexID);
58403                     if (!vertex || vertex.tags.noexit === 'yes') { return null; }
58404
58405                     var useLeftContinue = (whichEnd === 'start' && textDirection === 'ltr') ||
58406                         (whichEnd === 'end' && textDirection === 'rtl');
58407
58408                     return new validationIssueFix({
58409                         icon: 'iD-operation-continue' + (useLeftContinue ? '-left' : ''),
58410                         title: _t('issues.fix.continue_from_' + whichEnd + '.title'),
58411                         entityIds: [vertexID],
58412                         onClick: function(context) {
58413                             var wayId = this.issue.entityIds[0];
58414                             var way = context.hasEntity(wayId);
58415                             var vertexId = this.entityIds[0];
58416                             var vertex = context.hasEntity(vertexId);
58417
58418                             if (!way || !vertex) { return; }
58419
58420                             // make sure the vertex is actually visible and editable
58421                             var map = context.map();
58422                             if (!context.editable() || !map.trimmedExtent().contains(vertex.loc)) {
58423                                 map.zoomToEase(vertex);
58424                             }
58425
58426                             context.enter(
58427                                 modeDrawLine(context, wayId, context.graph(), 'line', way.affix(vertexId), true)
58428                             );
58429                         }
58430                     });
58431                 }
58432
58433             };
58434
58435             validation.type = type;
58436
58437             return validation;
58438         }
58439
58440         function validationFormatting() {
58441             var type = 'invalid_format';
58442
58443             var validation = function(entity) {
58444                 var issues = [];
58445
58446                 function isValidEmail(email) {
58447                     // Emails in OSM are going to be official so they should be pretty simple
58448                     // Using negated lists to better support all possible unicode characters (#6494)
58449                     var valid_email = /^[^\(\)\\,":;<>@\[\]]+@[^\(\)\\,":;<>@\[\]\.]+(?:\.[a-z0-9-]+)*$/i;
58450
58451                     // An empty value is also acceptable
58452                     return (!email || valid_email.test(email));
58453                 }
58454                 /*
58455                 function isSchemePresent(url) {
58456                     var valid_scheme = /^https?:\/\//i;
58457                     return (!url || valid_scheme.test(url));
58458                 }
58459                 */
58460                 function showReferenceEmail(selection) {
58461                     selection.selectAll('.issue-reference')
58462                         .data([0])
58463                         .enter()
58464                         .append('div')
58465                         .attr('class', 'issue-reference')
58466                         .text(_t('issues.invalid_format.email.reference'));
58467                 }
58468                 /*
58469                 function showReferenceWebsite(selection) {
58470                     selection.selectAll('.issue-reference')
58471                         .data([0])
58472                         .enter()
58473                         .append('div')
58474                         .attr('class', 'issue-reference')
58475                         .text(t('issues.invalid_format.website.reference'));
58476                 }
58477
58478                 if (entity.tags.website) {
58479                     // Multiple websites are possible
58480                     // If ever we support ES6, arrow functions make this nicer
58481                     var websites = entity.tags.website
58482                         .split(';')
58483                         .map(function(s) { return s.trim(); })
58484                         .filter(function(x) { return !isSchemePresent(x); });
58485
58486                     if (websites.length) {
58487                         issues.push(new validationIssue({
58488                             type: type,
58489                             subtype: 'website',
58490                             severity: 'warning',
58491                             message: function(context) {
58492                                 var entity = context.hasEntity(this.entityIds[0]);
58493                                 return entity ? t('issues.invalid_format.website.message' + this.data,
58494                                     { feature: utilDisplayLabel(entity, context.graph()), site: websites.join(', ') }) : '';
58495                             },
58496                             reference: showReferenceWebsite,
58497                             entityIds: [entity.id],
58498                             hash: websites.join(),
58499                             data: (websites.length > 1) ? '_multi' : ''
58500                         }));
58501                     }
58502                 }
58503                 */
58504                 if (entity.tags.email) {
58505                     // Multiple emails are possible
58506                     var emails = entity.tags.email
58507                         .split(';')
58508                         .map(function(s) { return s.trim(); })
58509                         .filter(function(x) { return !isValidEmail(x); });
58510
58511                     if (emails.length) {
58512                         issues.push(new validationIssue({
58513                             type: type,
58514                             subtype: 'email',
58515                             severity: 'warning',
58516                             message: function(context) {
58517                                 var entity = context.hasEntity(this.entityIds[0]);
58518                                 return entity ? _t('issues.invalid_format.email.message' + this.data,
58519                                     { feature: utilDisplayLabel(entity, context.graph()), email: emails.join(', ') }) : '';
58520                             },
58521                             reference: showReferenceEmail,
58522                             entityIds: [entity.id],
58523                             hash: emails.join(),
58524                             data: (emails.length > 1) ? '_multi' : ''
58525                         }));
58526                     }
58527                 }
58528
58529                 return issues;
58530             };
58531
58532             validation.type = type;
58533
58534             return validation;
58535         }
58536
58537         function validationHelpRequest(context) {
58538             var type = 'help_request';
58539
58540             var validation = function checkFixmeTag(entity) {
58541
58542                 if (!entity.tags.fixme) { return []; }
58543
58544                 // don't flag fixmes on features added by the user
58545                 if (entity.version === undefined) { return []; }
58546
58547                 if (entity.v !== undefined) {
58548                     var baseEntity = context.history().base().hasEntity(entity.id);
58549                     // don't flag fixmes added by the user on existing features
58550                     if (!baseEntity || !baseEntity.tags.fixme) { return []; }
58551                 }
58552
58553                 return [new validationIssue({
58554                     type: type,
58555                     subtype: 'fixme_tag',
58556                     severity: 'warning',
58557                     message: function(context) {
58558                         var entity = context.hasEntity(this.entityIds[0]);
58559                         return entity ? _t('issues.fixme_tag.message', { feature: utilDisplayLabel(entity, context.graph()) }) : '';
58560                     },
58561                     dynamicFixes: function() {
58562                         return [
58563                             new validationIssueFix({
58564                                 title: _t('issues.fix.address_the_concern.title')
58565                             })
58566                         ];
58567                     },
58568                     reference: showReference,
58569                     entityIds: [entity.id]
58570                 })];
58571
58572                 function showReference(selection) {
58573                     selection.selectAll('.issue-reference')
58574                         .data([0])
58575                         .enter()
58576                         .append('div')
58577                         .attr('class', 'issue-reference')
58578                         .text(_t('issues.fixme_tag.reference'));
58579                 }
58580             };
58581
58582             validation.type = type;
58583
58584             return validation;
58585         }
58586
58587         function validationImpossibleOneway() {
58588             var type = 'impossible_oneway';
58589
58590             var validation = function checkImpossibleOneway(entity, graph) {
58591
58592                 if (entity.type !== 'way' || entity.geometry(graph) !== 'line') { return []; }
58593
58594                 if (entity.isClosed()) { return []; }
58595
58596                 if (!typeForWay(entity)) { return []; }
58597
58598                 if (!isOneway(entity)) { return []; }
58599
58600                 var firstIssues = issuesForNode(entity, entity.first());
58601                 var lastIssues = issuesForNode(entity, entity.last());
58602
58603                 return firstIssues.concat(lastIssues);
58604
58605                 function typeForWay(way) {
58606                     if (way.geometry(graph) !== 'line') { return null; }
58607
58608                     if (osmRoutableHighwayTagValues[way.tags.highway]) { return 'highway'; }
58609                     if (osmFlowingWaterwayTagValues[way.tags.waterway]) { return 'waterway'; }
58610                     return null;
58611                 }
58612
58613                 function isOneway(way) {
58614                     if (way.tags.oneway === 'yes') { return true; }
58615                     if (way.tags.oneway) { return false; }
58616
58617                     for (var key in way.tags) {
58618                         if (osmOneWayTags[key] && osmOneWayTags[key][way.tags[key]]) {
58619                             return true;
58620                         }
58621                     }
58622                     return false;
58623                 }
58624
58625                 function nodeOccursMoreThanOnce(way, nodeID) {
58626                     var occurences = 0;
58627                     for (var index in way.nodes) {
58628                         if (way.nodes[index] === nodeID) {
58629                             occurences += 1;
58630                             if (occurences > 1) { return true; }
58631                         }
58632                     }
58633                     return false;
58634                 }
58635
58636                 function isConnectedViaOtherTypes(way, node) {
58637
58638                     var wayType = typeForWay(way);
58639
58640                     if (wayType === 'highway') {
58641                         // entrances are considered connected
58642                         if (node.tags.entrance && node.tags.entrance !== 'no') { return true; }
58643                         if (node.tags.amenity === 'parking_entrance') { return true; }
58644                     } else if (wayType === 'waterway') {
58645                         if (node.id === way.first()) {
58646                             // multiple waterways may start at the same spring
58647                             if (node.tags.natural === 'spring') { return true; }
58648                         } else {
58649                             // multiple waterways may end at the same drain
58650                             if (node.tags.manhole === 'drain') { return true; }
58651                         }
58652                     }
58653
58654                     return graph.parentWays(node).some(function(parentWay) {
58655                         if (parentWay.id === way.id) { return false; }
58656
58657                         if (wayType === 'highway') {
58658
58659                             // allow connections to highway areas
58660                             if (parentWay.geometry(graph) === 'area' &&
58661                                 osmRoutableHighwayTagValues[parentWay.tags.highway]) { return true; }
58662
58663                             // count connections to ferry routes as connected
58664                             if (parentWay.tags.route === 'ferry') { return true; }
58665
58666                             return graph.parentRelations(parentWay).some(function(parentRelation) {
58667                                 if (parentRelation.tags.type === 'route' &&
58668                                     parentRelation.tags.route === 'ferry') { return true; }
58669
58670                                 // allow connections to highway multipolygons
58671                                 return parentRelation.isMultipolygon() && osmRoutableHighwayTagValues[parentRelation.tags.highway];
58672                             });
58673                         } else if (wayType === 'waterway') {
58674                             // multiple waterways may start or end at a water body at the same node
58675                             if (parentWay.tags.natural === 'water' ||
58676                                 parentWay.tags.natural === 'coastline') { return true; }
58677                         }
58678                         return false;
58679                     });
58680                 }
58681
58682                 function issuesForNode(way, nodeID) {
58683
58684                     var isFirst = nodeID === way.first();
58685
58686                     var wayType = typeForWay(way);
58687
58688                     // ignore if this way is self-connected at this node
58689                     if (nodeOccursMoreThanOnce(way, nodeID)) { return []; }
58690
58691                     var osm = services.osm;
58692                     if (!osm) { return []; }
58693
58694                     var node = graph.hasEntity(nodeID);
58695
58696                     // ignore if this node or its tile are unloaded
58697                     if (!node || !osm.isDataLoaded(node.loc)) { return []; }
58698
58699                     if (isConnectedViaOtherTypes(way, node)) { return []; }
58700
58701                     var attachedWaysOfSameType = graph.parentWays(node).filter(function(parentWay) {
58702                         if (parentWay.id === way.id) { return false; }
58703                         return typeForWay(parentWay) === wayType;
58704                     });
58705
58706                     // assume it's okay for waterways to start or end disconnected for now
58707                     if (wayType === 'waterway' && attachedWaysOfSameType.length === 0) { return []; }
58708
58709                     var attachedOneways = attachedWaysOfSameType.filter(function(attachedWay) {
58710                         return isOneway(attachedWay);
58711                     });
58712
58713                     // ignore if the way is connected to some non-oneway features
58714                     if (attachedOneways.length < attachedWaysOfSameType.length) { return []; }
58715
58716                     if (attachedOneways.length) {
58717                         var connectedEndpointsOkay = attachedOneways.some(function(attachedOneway) {
58718                             if ((isFirst ? attachedOneway.first() : attachedOneway.last()) !== nodeID) { return true; }
58719                             if (nodeOccursMoreThanOnce(attachedOneway, nodeID)) { return true; }
58720                             return false;
58721                         });
58722                         if (connectedEndpointsOkay) { return []; }
58723                     }
58724
58725                     var placement = isFirst ? 'start' : 'end',
58726                         messageID = wayType + '.',
58727                         referenceID = wayType + '.';
58728
58729                     if (wayType === 'waterway') {
58730                         messageID += 'connected.' + placement;
58731                         referenceID += 'connected';
58732                     } else {
58733                         messageID += placement;
58734                         referenceID += placement;
58735                     }
58736
58737                     return [new validationIssue({
58738                         type: type,
58739                         subtype: wayType,
58740                         severity: 'warning',
58741                         message: function(context) {
58742                             var entity = context.hasEntity(this.entityIds[0]);
58743                             return entity ? _t('issues.impossible_oneway.' + messageID + '.message', {
58744                                 feature: utilDisplayLabel(entity, context.graph())
58745                             }) : '';
58746                         },
58747                         reference: getReference(referenceID),
58748                         entityIds: [way.id, node.id],
58749                         dynamicFixes: function() {
58750
58751                             var fixes = [];
58752
58753                             if (attachedOneways.length) {
58754                                 fixes.push(new validationIssueFix({
58755                                     icon: 'iD-operation-reverse',
58756                                     title: _t('issues.fix.reverse_feature.title'),
58757                                     entityIds: [way.id],
58758                                     onClick: function(context) {
58759                                         var id = this.issue.entityIds[0];
58760                                         context.perform(actionReverse(id), _t('operations.reverse.annotation'));
58761                                     }
58762                                 }));
58763                             }
58764                             if (node.tags.noexit !== 'yes') {
58765                                 var textDirection = _mainLocalizer.textDirection();
58766                                 var useLeftContinue = (isFirst && textDirection === 'ltr') ||
58767                                     (!isFirst && textDirection === 'rtl');
58768                                 fixes.push(new validationIssueFix({
58769                                     icon: 'iD-operation-continue' + (useLeftContinue ? '-left' : ''),
58770                                     title: _t('issues.fix.continue_from_' + (isFirst ? 'start' : 'end') + '.title'),
58771                                     onClick: function(context) {
58772                                         var entityID = this.issue.entityIds[0];
58773                                         var vertexID = this.issue.entityIds[1];
58774                                         var way = context.entity(entityID);
58775                                         var vertex = context.entity(vertexID);
58776                                         continueDrawing(way, vertex, context);
58777                                     }
58778                                 }));
58779                             }
58780
58781                             return fixes;
58782                         },
58783                         loc: node.loc
58784                     })];
58785
58786                     function getReference(referenceID) {
58787                         return function showReference(selection) {
58788                             selection.selectAll('.issue-reference')
58789                                 .data([0])
58790                                 .enter()
58791                                 .append('div')
58792                                 .attr('class', 'issue-reference')
58793                                 .text(_t('issues.impossible_oneway.' + referenceID + '.reference'));
58794                         };
58795                     }
58796                 }
58797             };
58798
58799             function continueDrawing(way, vertex, context) {
58800                 // make sure the vertex is actually visible and editable
58801                 var map = context.map();
58802                 if (!context.editable() || !map.trimmedExtent().contains(vertex.loc)) {
58803                     map.zoomToEase(vertex);
58804                 }
58805
58806                 context.enter(
58807                     modeDrawLine(context, way.id, context.graph(), 'line', way.affix(vertex.id), true)
58808                 );
58809             }
58810
58811             validation.type = type;
58812
58813             return validation;
58814         }
58815
58816         function validationIncompatibleSource() {
58817             var type = 'incompatible_source';
58818             var invalidSources = [
58819                 {
58820                     id:'google', regex:'google', exceptRegex: 'books.google|Google Books|drive.google|googledrive|Google Drive'
58821                 }
58822             ];
58823
58824             var validation = function checkIncompatibleSource(entity) {
58825
58826                 var entitySources = entity.tags && entity.tags.source && entity.tags.source.split(';');
58827
58828                 if (!entitySources) { return []; }
58829
58830                 var issues = [];
58831
58832                 invalidSources.forEach(function(invalidSource) {
58833
58834                     var hasInvalidSource = entitySources.some(function(source) {
58835                         if (!source.match(new RegExp(invalidSource.regex, 'i'))) { return false; }
58836                         if (invalidSource.exceptRegex && source.match(new RegExp(invalidSource.exceptRegex, 'i'))) { return false; }
58837                         return true;
58838                     });
58839
58840                     if (!hasInvalidSource) { return; }
58841
58842                     issues.push(new validationIssue({
58843                         type: type,
58844                         severity: 'warning',
58845                         message: function(context) {
58846                             var entity = context.hasEntity(this.entityIds[0]);
58847                             return entity ? _t('issues.incompatible_source.' + invalidSource.id + '.feature.message', {
58848                                 feature: utilDisplayLabel(entity, context.graph())
58849                             }) : '';
58850                         },
58851                         reference: getReference(invalidSource.id),
58852                         entityIds: [entity.id],
58853                         dynamicFixes: function() {
58854                             return [
58855                                 new validationIssueFix({
58856                                     title: _t('issues.fix.remove_proprietary_data.title')
58857                                 })
58858                             ];
58859                         }
58860                     }));
58861                 });
58862
58863                 return issues;
58864
58865
58866                 function getReference(id) {
58867                     return function showReference(selection) {
58868                         selection.selectAll('.issue-reference')
58869                             .data([0])
58870                             .enter()
58871                             .append('div')
58872                             .attr('class', 'issue-reference')
58873                             .text(_t('issues.incompatible_source.' + id + '.reference'));
58874                     };
58875                 }
58876             };
58877
58878             validation.type = type;
58879
58880             return validation;
58881         }
58882
58883         function validationMaprules() {
58884             var type = 'maprules';
58885
58886             var validation = function checkMaprules(entity, graph) {
58887                 if (!services.maprules) { return []; }
58888
58889                 var rules = services.maprules.validationRules();
58890                 var issues = [];
58891
58892                 for (var i = 0; i < rules.length; i++) {
58893                     var rule = rules[i];
58894                     rule.findIssues(entity, graph, issues);
58895                 }
58896
58897                 return issues;
58898             };
58899
58900
58901             validation.type = type;
58902
58903             return validation;
58904         }
58905
58906         function validationMismatchedGeometry() {
58907             var type = 'mismatched_geometry';
58908
58909             function tagSuggestingLineIsArea(entity) {
58910                 if (entity.type !== 'way' || entity.isClosed()) { return null; }
58911
58912                 var tagSuggestingArea = entity.tagSuggestingArea();
58913                 if (!tagSuggestingArea) {
58914                     return null;
58915                 }
58916
58917                 var asLine = _mainPresetIndex.matchTags(tagSuggestingArea, 'line');
58918                 var asArea = _mainPresetIndex.matchTags(tagSuggestingArea, 'area');
58919                 if (asLine && asArea && (asLine === asArea)) {
58920                     // these tags also allow lines and making this an area wouldn't matter
58921                     return null;
58922                 }
58923
58924                 return tagSuggestingArea;
58925             }
58926
58927
58928             function makeConnectEndpointsFixOnClick(way, graph) {
58929                 // must have at least three nodes to close this automatically
58930                 if (way.nodes.length < 3) { return null; }
58931
58932                 var nodes = graph.childNodes(way), testNodes;
58933                 var firstToLastDistanceMeters = geoSphericalDistance(nodes[0].loc, nodes[nodes.length-1].loc);
58934
58935                 // if the distance is very small, attempt to merge the endpoints
58936                 if (firstToLastDistanceMeters < 0.75) {
58937                     testNodes = nodes.slice();   // shallow copy
58938                     testNodes.pop();
58939                     testNodes.push(testNodes[0]);
58940                     // make sure this will not create a self-intersection
58941                     if (!geoHasSelfIntersections(testNodes, testNodes[0].id)) {
58942                         return function(context) {
58943                             var way = context.entity(this.issue.entityIds[0]);
58944                             context.perform(
58945                                 actionMergeNodes([way.nodes[0], way.nodes[way.nodes.length-1]], nodes[0].loc),
58946                                 _t('issues.fix.connect_endpoints.annotation')
58947                             );
58948                         };
58949                     }
58950                 }
58951
58952                 // if the points were not merged, attempt to close the way
58953                 testNodes = nodes.slice();   // shallow copy
58954                 testNodes.push(testNodes[0]);
58955                 // make sure this will not create a self-intersection
58956                 if (!geoHasSelfIntersections(testNodes, testNodes[0].id)) {
58957                     return function(context) {
58958                         var wayId = this.issue.entityIds[0];
58959                         var way = context.entity(wayId);
58960                         var nodeId = way.nodes[0];
58961                         var index = way.nodes.length;
58962                         context.perform(
58963                             actionAddVertex(wayId, nodeId, index),
58964                             _t('issues.fix.connect_endpoints.annotation')
58965                         );
58966                     };
58967                 }
58968             }
58969
58970             function lineTaggedAsAreaIssue(entity) {
58971
58972                 var tagSuggestingArea = tagSuggestingLineIsArea(entity);
58973                 if (!tagSuggestingArea) { return null; }
58974
58975                 return new validationIssue({
58976                     type: type,
58977                     subtype: 'area_as_line',
58978                     severity: 'warning',
58979                     message: function(context) {
58980                         var entity = context.hasEntity(this.entityIds[0]);
58981                         return entity ? _t('issues.tag_suggests_area.message', {
58982                             feature: utilDisplayLabel(entity, context.graph()),
58983                             tag: utilTagText({ tags: tagSuggestingArea })
58984                         }) : '';
58985                     },
58986                     reference: showReference,
58987                     entityIds: [entity.id],
58988                     hash: JSON.stringify(tagSuggestingArea),
58989                     dynamicFixes: function(context) {
58990
58991                         var fixes = [];
58992
58993                         var entity = context.entity(this.entityIds[0]);
58994                         var connectEndsOnClick = makeConnectEndpointsFixOnClick(entity, context.graph());
58995
58996                         fixes.push(new validationIssueFix({
58997                             title: _t('issues.fix.connect_endpoints.title'),
58998                             onClick: connectEndsOnClick
58999                         }));
59000
59001                         fixes.push(new validationIssueFix({
59002                             icon: 'iD-operation-delete',
59003                             title: _t('issues.fix.remove_tag.title'),
59004                             onClick: function(context) {
59005                                 var entityId = this.issue.entityIds[0];
59006                                 var entity = context.entity(entityId);
59007                                 var tags = Object.assign({}, entity.tags);  // shallow copy
59008                                 for (var key in tagSuggestingArea) {
59009                                     delete tags[key];
59010                                 }
59011                                 context.perform(
59012                                     actionChangeTags(entityId, tags),
59013                                     _t('issues.fix.remove_tag.annotation')
59014                                 );
59015                             }
59016                         }));
59017
59018                         return fixes;
59019                     }
59020                 });
59021
59022
59023                 function showReference(selection) {
59024                     selection.selectAll('.issue-reference')
59025                         .data([0])
59026                         .enter()
59027                         .append('div')
59028                         .attr('class', 'issue-reference')
59029                         .text(_t('issues.tag_suggests_area.reference'));
59030                 }
59031             }
59032
59033             function vertexTaggedAsPointIssue(entity, graph) {
59034                 // we only care about nodes
59035                 if (entity.type !== 'node') { return null; }
59036
59037                 // ignore tagless points
59038                 if (Object.keys(entity.tags).length === 0) { return null; }
59039
59040                 // address lines are special so just ignore them
59041                 if (entity.isOnAddressLine(graph)) { return null; }
59042
59043                 var geometry = entity.geometry(graph);
59044                 var allowedGeometries = osmNodeGeometriesForTags(entity.tags);
59045
59046                 if (geometry === 'point' && !allowedGeometries.point && allowedGeometries.vertex) {
59047
59048                     return new validationIssue({
59049                         type: type,
59050                         subtype: 'vertex_as_point',
59051                         severity: 'warning',
59052                         message: function(context) {
59053                             var entity = context.hasEntity(this.entityIds[0]);
59054                             return entity ? _t('issues.vertex_as_point.message', {
59055                                 feature: utilDisplayLabel(entity, context.graph())
59056                             }) : '';
59057                         },
59058                         reference: function showReference(selection) {
59059                             selection.selectAll('.issue-reference')
59060                                 .data([0])
59061                                 .enter()
59062                                 .append('div')
59063                                 .attr('class', 'issue-reference')
59064                                 .text(_t('issues.vertex_as_point.reference'));
59065                         },
59066                         entityIds: [entity.id]
59067                     });
59068
59069                 } else if (geometry === 'vertex' && !allowedGeometries.vertex && allowedGeometries.point) {
59070
59071                     return new validationIssue({
59072                         type: type,
59073                         subtype: 'point_as_vertex',
59074                         severity: 'warning',
59075                         message: function(context) {
59076                             var entity = context.hasEntity(this.entityIds[0]);
59077                             return entity ? _t('issues.point_as_vertex.message', {
59078                                 feature: utilDisplayLabel(entity, context.graph())
59079                             }) : '';
59080                         },
59081                         reference: function showReference(selection) {
59082                             selection.selectAll('.issue-reference')
59083                                 .data([0])
59084                                 .enter()
59085                                 .append('div')
59086                                 .attr('class', 'issue-reference')
59087                                 .text(_t('issues.point_as_vertex.reference'));
59088                         },
59089                         entityIds: [entity.id],
59090                         dynamicFixes: function(context) {
59091
59092                             var entityId = this.entityIds[0];
59093
59094                             var extractOnClick = null;
59095                             if (!context.hasHiddenConnections(entityId)) {
59096
59097                                 extractOnClick = function(context) {
59098                                     var entityId = this.issue.entityIds[0];
59099                                     var action = actionExtract(entityId);
59100                                     context.perform(
59101                                         action,
59102                                         _t('operations.extract.annotation.single')
59103                                     );
59104                                     // re-enter mode to trigger updates
59105                                     context.enter(modeSelect(context, [action.getExtractedNodeID()]));
59106                                 };
59107                             }
59108
59109                             return [
59110                                 new validationIssueFix({
59111                                     icon: 'iD-operation-extract',
59112                                     title: _t('issues.fix.extract_point.title'),
59113                                     onClick: extractOnClick
59114                                 })
59115                             ];
59116                         }
59117                     });
59118                 }
59119
59120                 return null;
59121             }
59122
59123             function unclosedMultipolygonPartIssues(entity, graph) {
59124
59125                 if (entity.type !== 'relation' ||
59126                     !entity.isMultipolygon() ||
59127                     entity.isDegenerate() ||
59128                     // cannot determine issues for incompletely-downloaded relations
59129                     !entity.isComplete(graph)) { return null; }
59130
59131                 var sequences = osmJoinWays(entity.members, graph);
59132
59133                 var issues = [];
59134
59135                 for (var i in sequences) {
59136                     var sequence = sequences[i];
59137
59138                     if (!sequence.nodes) { continue; }
59139
59140                     var firstNode = sequence.nodes[0];
59141                     var lastNode = sequence.nodes[sequence.nodes.length - 1];
59142
59143                     // part is closed if the first and last nodes are the same
59144                     if (firstNode === lastNode) { continue; }
59145
59146                     var issue = new validationIssue({
59147                         type: type,
59148                         subtype: 'unclosed_multipolygon_part',
59149                         severity: 'warning',
59150                         message: function(context) {
59151                             var entity = context.hasEntity(this.entityIds[0]);
59152                             return entity ? _t('issues.unclosed_multipolygon_part.message', {
59153                                 feature: utilDisplayLabel(entity, context.graph())
59154                             }) : '';
59155                         },
59156                         reference: showReference,
59157                         loc: sequence.nodes[0].loc,
59158                         entityIds: [entity.id],
59159                         hash: sequence.map(function(way) {
59160                             return way.id;
59161                         }).join()
59162                     });
59163                     issues.push(issue);
59164                 }
59165
59166                 return issues;
59167
59168                 function showReference(selection) {
59169                     selection.selectAll('.issue-reference')
59170                         .data([0])
59171                         .enter()
59172                         .append('div')
59173                         .attr('class', 'issue-reference')
59174                         .text(_t('issues.unclosed_multipolygon_part.reference'));
59175                 }
59176             }
59177
59178             var validation = function checkMismatchedGeometry(entity, graph) {
59179                 var issues = [
59180                     vertexTaggedAsPointIssue(entity, graph),
59181                     lineTaggedAsAreaIssue(entity)
59182                 ];
59183                 issues = issues.concat(unclosedMultipolygonPartIssues(entity, graph));
59184                 return issues.filter(Boolean);
59185             };
59186
59187             validation.type = type;
59188
59189             return validation;
59190         }
59191
59192         function validationMissingRole() {
59193             var type = 'missing_role';
59194
59195             var validation = function checkMissingRole(entity, graph) {
59196                 var issues = [];
59197                 if (entity.type === 'way') {
59198                     graph.parentRelations(entity).forEach(function(relation) {
59199                         if (!relation.isMultipolygon()) { return; }
59200
59201                         var member = relation.memberById(entity.id);
59202                         if (member && isMissingRole(member)) {
59203                             issues.push(makeIssue(entity, relation, member));
59204                         }
59205                     });
59206                 } else if (entity.type === 'relation' && entity.isMultipolygon()) {
59207                     entity.indexedMembers().forEach(function(member) {
59208                         var way = graph.hasEntity(member.id);
59209                         if (way && isMissingRole(member)) {
59210                             issues.push(makeIssue(way, entity, member));
59211                         }
59212                     });
59213                 }
59214
59215                 return issues;
59216             };
59217
59218
59219             function isMissingRole(member) {
59220                 return !member.role || !member.role.trim().length;
59221             }
59222
59223
59224             function makeIssue(way, relation, member) {
59225                 return new validationIssue({
59226                     type: type,
59227                     severity: 'warning',
59228                     message: function(context) {
59229                         var member = context.hasEntity(this.entityIds[1]),
59230                             relation = context.hasEntity(this.entityIds[0]);
59231                         return (member && relation) ? _t('issues.missing_role.message', {
59232                             member: utilDisplayLabel(member, context.graph()),
59233                             relation: utilDisplayLabel(relation, context.graph())
59234                         }) : '';
59235                     },
59236                     reference: showReference,
59237                     entityIds: [relation.id, way.id],
59238                     data: {
59239                         member: member
59240                     },
59241                     hash: member.index.toString(),
59242                     dynamicFixes: function() {
59243                         return [
59244                             makeAddRoleFix('inner'),
59245                             makeAddRoleFix('outer'),
59246                             new validationIssueFix({
59247                                 icon: 'iD-operation-delete',
59248                                 title: _t('issues.fix.remove_from_relation.title'),
59249                                 onClick: function(context) {
59250                                     context.perform(
59251                                         actionDeleteMember(this.issue.entityIds[0], this.issue.data.member.index),
59252                                         _t('operations.delete_member.annotation')
59253                                     );
59254                                 }
59255                             })
59256                         ];
59257                     }
59258                 });
59259
59260
59261                 function showReference(selection) {
59262                     selection.selectAll('.issue-reference')
59263                         .data([0])
59264                         .enter()
59265                         .append('div')
59266                         .attr('class', 'issue-reference')
59267                         .text(_t('issues.missing_role.multipolygon.reference'));
59268                 }
59269             }
59270
59271
59272             function makeAddRoleFix(role) {
59273                 return new validationIssueFix({
59274                     title: _t('issues.fix.set_as_' + role + '.title'),
59275                     onClick: function(context) {
59276                         var oldMember = this.issue.data.member;
59277                         var member = { id: this.issue.entityIds[1], type: oldMember.type, role: role };
59278                         context.perform(
59279                             actionChangeMember(this.issue.entityIds[0], member, oldMember.index),
59280                             _t('operations.change_role.annotation')
59281                         );
59282                     }
59283                 });
59284             }
59285
59286             validation.type = type;
59287
59288             return validation;
59289         }
59290
59291         function validationMissingTag(context) {
59292             var type = 'missing_tag';
59293
59294             function hasDescriptiveTags(entity, graph) {
59295                 var keys = Object.keys(entity.tags)
59296                     .filter(function(k) {
59297                         if (k === 'area' || k === 'name') {
59298                             return false;
59299                         } else {
59300                             return osmIsInterestingTag(k);
59301                         }
59302                     });
59303
59304                 if (entity.type === 'relation' &&
59305                     keys.length === 1 &&
59306                     entity.tags.type === 'multipolygon') {
59307                     // this relation's only interesting tag just says its a multipolygon,
59308                     // which is not descriptive enough
59309
59310                     // It's okay for a simple multipolygon to have no descriptive tags
59311                     // if its outer way has them (old model, see `outdated_tags.js`)
59312                     return osmOldMultipolygonOuterMemberOfRelation(entity, graph);
59313                 }
59314
59315                 return keys.length > 0;
59316             }
59317
59318             function isUnknownRoad(entity) {
59319                 return entity.type === 'way' && entity.tags.highway === 'road';
59320             }
59321
59322             function isUntypedRelation(entity) {
59323                 return entity.type === 'relation' && !entity.tags.type;
59324             }
59325
59326             var validation = function checkMissingTag(entity, graph) {
59327
59328                 var subtype;
59329
59330                 var osm = context.connection();
59331                 var isUnloadedNode = entity.type === 'node' && osm && !osm.isDataLoaded(entity.loc);
59332
59333                 // we can't know if the node is a vertex if the tile is undownloaded
59334                 if (!isUnloadedNode &&
59335                     // allow untagged nodes that are part of ways
59336                     entity.geometry(graph) !== 'vertex' &&
59337                     // allow untagged entities that are part of relations
59338                     !entity.hasParentRelations(graph)) {
59339
59340                     if (Object.keys(entity.tags).length === 0) {
59341                         subtype = 'any';
59342                     } else if (!hasDescriptiveTags(entity, graph)) {
59343                         subtype = 'descriptive';
59344                     } else if (isUntypedRelation(entity)) {
59345                         subtype = 'relation_type';
59346                     }
59347                 }
59348
59349                 // flag an unknown road even if it's a member of a relation
59350                 if (!subtype && isUnknownRoad(entity)) {
59351                     subtype = 'highway_classification';
59352                 }
59353
59354                 if (!subtype) { return []; }
59355
59356                 var messageID = subtype === 'highway_classification' ? 'unknown_road' : 'missing_tag.' + subtype;
59357                 var referenceID = subtype === 'highway_classification' ? 'unknown_road' : 'missing_tag';
59358
59359                 // can always delete if the user created it in the first place..
59360                 var canDelete = (entity.version === undefined || entity.v !== undefined);
59361                 var severity = (canDelete && subtype !== 'highway_classification') ? 'error' : 'warning';
59362
59363                 return [new validationIssue({
59364                     type: type,
59365                     subtype: subtype,
59366                     severity: severity,
59367                     message: function(context) {
59368                         var entity = context.hasEntity(this.entityIds[0]);
59369                         return entity ? _t('issues.' + messageID + '.message', {
59370                             feature: utilDisplayLabel(entity, context.graph())
59371                         }) : '';
59372                     },
59373                     reference: showReference,
59374                     entityIds: [entity.id],
59375                     dynamicFixes: function(context) {
59376
59377                         var fixes = [];
59378
59379                         var selectFixType = subtype === 'highway_classification' ? 'select_road_type' : 'select_preset';
59380
59381                         fixes.push(new validationIssueFix({
59382                             icon: 'iD-icon-search',
59383                             title: _t('issues.fix.' + selectFixType + '.title'),
59384                             onClick: function(context) {
59385                                 context.ui().sidebar.showPresetList();
59386                             }
59387                         }));
59388
59389                         var deleteOnClick;
59390
59391                         var id = this.entityIds[0];
59392                         var operation = operationDelete(context, [id]);
59393                         var disabledReasonID = operation.disabled();
59394                         if (!disabledReasonID) {
59395                             deleteOnClick = function(context) {
59396                                 var id = this.issue.entityIds[0];
59397                                 var operation = operationDelete(context, [id]);
59398                                 if (!operation.disabled()) {
59399                                     operation();
59400                                 }
59401                             };
59402                         }
59403
59404                         fixes.push(
59405                             new validationIssueFix({
59406                                 icon: 'iD-operation-delete',
59407                                 title: _t('issues.fix.delete_feature.title'),
59408                                 disabledReason: disabledReasonID ? _t('operations.delete.' + disabledReasonID + '.single') : undefined,
59409                                 onClick: deleteOnClick
59410                             })
59411                         );
59412
59413                         return fixes;
59414                     }
59415                 })];
59416
59417                 function showReference(selection) {
59418                     selection.selectAll('.issue-reference')
59419                         .data([0])
59420                         .enter()
59421                         .append('div')
59422                         .attr('class', 'issue-reference')
59423                         .text(_t('issues.' + referenceID + '.reference'));
59424                 }
59425             };
59426
59427             validation.type = type;
59428
59429             return validation;
59430         }
59431
59432         // remove spaces, punctuation, diacritics
59433         var simplify = function (str) {
59434           return diacritics.remove(
59435             str
59436               .replace(/&/g, 'and')
59437               .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,'')
59438               .toLowerCase()
59439           );
59440         };
59441
59442         // toParts - split a name-suggestion-index key into parts
59443         // {
59444         //   kvnd:        "amenity/fast_food|Thaï Express~(North America)",
59445         //   kvn:         "amenity/fast_food|Thaï Express",
59446         //   kv:          "amenity/fast_food",
59447         //   k:           "amenity",
59448         //   v:           "fast_food",
59449         //   n:           "Thaï Express",
59450         //   d:           "(North America)",
59451         //   nsimple:     "thaiexpress",
59452         //   kvnnsimple:  "amenity/fast_food|thaiexpress"
59453         // }
59454         var to_parts = function (kvnd) {
59455           var parts = {};
59456           parts.kvnd = kvnd;
59457
59458           var kvndparts = kvnd.split('~', 2);
59459           if (kvndparts.length > 1) { parts.d = kvndparts[1]; }
59460
59461           parts.kvn = kvndparts[0];
59462           var kvnparts = parts.kvn.split('|', 2);
59463           if (kvnparts.length > 1) { parts.n = kvnparts[1]; }
59464
59465           parts.kv = kvnparts[0];
59466           var kvparts = parts.kv.split('/', 2);
59467           parts.k = kvparts[0];
59468           parts.v = kvparts[1];
59469
59470           parts.nsimple = simplify(parts.n);
59471           parts.kvnsimple = parts.kv + '|' + parts.nsimple;
59472           return parts;
59473         };
59474
59475         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"]};
59476         var require$$0 = {
59477         matchGroups: matchGroups
59478         };
59479
59480         var matchGroups$1 = require$$0.matchGroups;
59481
59482
59483         var matcher$1 = function () {
59484           var _warnings = [];  // array of match conflict pairs
59485           var _ambiguous = {};
59486           var _matchIndex = {};
59487           var matcher = {};
59488
59489
59490           // Create an index of all the keys/simplenames for fast matching
59491           matcher.buildMatchIndex = function (brands) {
59492             // two passes - once for primary names, once for secondary/alternate names
59493             Object.keys(brands).forEach(function (kvnd) { return insertNames(kvnd, 'primary'); });
59494             Object.keys(brands).forEach(function (kvnd) { return insertNames(kvnd, 'secondary'); });
59495
59496
59497             function insertNames(kvnd, which) {
59498               var obj = brands[kvnd];
59499               var parts = to_parts(kvnd);
59500
59501               // Exit early for ambiguous names in the second pass.
59502               // They were collected in the first pass and we don't gather alt names for them.
59503               if (which === 'secondary' && parts.d) { return; }
59504
59505
59506               if (obj.countryCodes) {
59507                 parts.countryCodes = obj.countryCodes.slice();  // copy
59508               }
59509
59510               var nomatches = (obj.nomatch || []);
59511               if (nomatches.some(function (s) { return s === kvnd; })) {
59512                 console.log(("WARNING match/nomatch conflict for " + kvnd));
59513                 return;
59514               }
59515
59516               var match_kv = [parts.kv]
59517                 .concat(obj.matchTags || [])
59518                 .concat([((parts.k) + "/yes"), "building/yes"])   // #3454 - match some generic tags
59519                 .map(function (s) { return s.toLowerCase(); });
59520
59521               var match_nsimple = [];
59522               if (which === 'primary') {
59523                 match_nsimple = [parts.n]
59524                   .concat(obj.matchNames || [])
59525                   .concat(obj.tags.official_name || [])   // #2732 - match alternate names
59526                   .map(simplify);
59527
59528               } else if (which === 'secondary') {
59529                 match_nsimple = []
59530                   .concat(obj.tags.alt_name || [])        // #2732 - match alternate names
59531                   .concat(obj.tags.short_name || [])      // #2732 - match alternate names
59532                   .map(simplify);
59533               }
59534
59535               if (!match_nsimple.length) { return; }  // nothing to do
59536
59537               match_kv.forEach(function (kv) {
59538                 match_nsimple.forEach(function (nsimple) {
59539                   if (parts.d) {
59540                     // Known ambiguous names with disambiguation string ~(USA) / ~(Canada)
59541                     // FIXME: Name collisions will overwrite the initial entry (ok for now)
59542                     if (!_ambiguous[kv]) { _ambiguous[kv] = {}; }
59543                     _ambiguous[kv][nsimple] = parts;
59544
59545                   } else {
59546                     // Names we mostly expect to be unique..
59547                     if (!_matchIndex[kv]) { _matchIndex[kv] = {}; }
59548
59549                     var m = _matchIndex[kv][nsimple];
59550                     if (m) {  // There already is a match for this name, skip it
59551                       // Warn if we detect collisions in a primary name.
59552                       // Skip warning if a secondary name or a generic `*=yes` tag - #2972 / #3454
59553                       if (which === 'primary' && !/\/yes$/.test(kv)) {
59554                         _warnings.push([m.kvnd, (kvnd + " (" + kv + "/" + nsimple + ")")]);
59555                       }
59556                     } else {
59557                       _matchIndex[kv][nsimple] = parts;   // insert
59558                     }
59559                   }
59560                 });
59561               });
59562
59563             }
59564           };
59565
59566
59567           // pass a `key`, `value`, `name` and return the best match,
59568           // `countryCode` optional (if supplied, must match that too)
59569           matcher.matchKVN = function (key, value, name, countryCode) {
59570             return matcher.matchParts(to_parts((key + "/" + value + "|" + name)), countryCode);
59571           };
59572
59573
59574           // pass a parts object and return the best match,
59575           // `countryCode` optional (if supplied, must match that too)
59576           matcher.matchParts = function (parts, countryCode) {
59577             var match = null;
59578             var inGroup = false;
59579
59580             // fixme: we currently return a single match for ambiguous
59581             match = _ambiguous[parts.kv] && _ambiguous[parts.kv][parts.nsimple];
59582             if (match && matchesCountryCode(match)) { return match; }
59583
59584             // try to return an exact match
59585             match = _matchIndex[parts.kv] && _matchIndex[parts.kv][parts.nsimple];
59586             if (match && matchesCountryCode(match)) { return match; }
59587
59588             // look in match groups
59589             for (var mg in matchGroups$1) {
59590               var matchGroup = matchGroups$1[mg];
59591               match = null;
59592               inGroup = false;
59593
59594               for (var i = 0; i < matchGroup.length; i++) {
59595                 var otherkv = matchGroup[i].toLowerCase();
59596                 if (!inGroup) {
59597                   inGroup = otherkv === parts.kv;
59598                 }
59599                 if (!match) {
59600                   // fixme: we currently return a single match for ambiguous
59601                   match = _ambiguous[otherkv] && _ambiguous[otherkv][parts.nsimple];
59602                 }
59603                 if (!match) {
59604                   match = _matchIndex[otherkv] && _matchIndex[otherkv][parts.nsimple];
59605                 }
59606
59607                 if (match && !matchesCountryCode(match)) {
59608                   match = null;
59609                 }
59610
59611                 if (inGroup && match) {
59612                   return match;
59613                 }
59614               }
59615             }
59616
59617             return null;
59618
59619             function matchesCountryCode(match) {
59620               if (!countryCode) { return true; }
59621               if (!match.countryCodes) { return true; }
59622               return match.countryCodes.indexOf(countryCode) !== -1;
59623             }
59624           };
59625
59626
59627           matcher.getWarnings = function () { return _warnings; };
59628
59629           return matcher;
59630         };
59631
59632         var quickselect$1 = createCommonjsModule(function (module, exports) {
59633         (function (global, factory) {
59634                  module.exports = factory() ;
59635         }(commonjsGlobal, (function () {
59636         function quickselect(arr, k, left, right, compare) {
59637             quickselectStep(arr, k, left || 0, right || (arr.length - 1), compare || defaultCompare);
59638         }
59639
59640         function quickselectStep(arr, k, left, right, compare) {
59641
59642             while (right > left) {
59643                 if (right - left > 600) {
59644                     var n = right - left + 1;
59645                     var m = k - left + 1;
59646                     var z = Math.log(n);
59647                     var s = 0.5 * Math.exp(2 * z / 3);
59648                     var sd = 0.5 * Math.sqrt(z * s * (n - s) / n) * (m - n / 2 < 0 ? -1 : 1);
59649                     var newLeft = Math.max(left, Math.floor(k - m * s / n + sd));
59650                     var newRight = Math.min(right, Math.floor(k + (n - m) * s / n + sd));
59651                     quickselectStep(arr, k, newLeft, newRight, compare);
59652                 }
59653
59654                 var t = arr[k];
59655                 var i = left;
59656                 var j = right;
59657
59658                 swap(arr, left, k);
59659                 if (compare(arr[right], t) > 0) { swap(arr, left, right); }
59660
59661                 while (i < j) {
59662                     swap(arr, i, j);
59663                     i++;
59664                     j--;
59665                     while (compare(arr[i], t) < 0) { i++; }
59666                     while (compare(arr[j], t) > 0) { j--; }
59667                 }
59668
59669                 if (compare(arr[left], t) === 0) { swap(arr, left, j); }
59670                 else {
59671                     j++;
59672                     swap(arr, j, right);
59673                 }
59674
59675                 if (j <= k) { left = j + 1; }
59676                 if (k <= j) { right = j - 1; }
59677             }
59678         }
59679
59680         function swap(arr, i, j) {
59681             var tmp = arr[i];
59682             arr[i] = arr[j];
59683             arr[j] = tmp;
59684         }
59685
59686         function defaultCompare(a, b) {
59687             return a < b ? -1 : a > b ? 1 : 0;
59688         }
59689
59690         return quickselect;
59691
59692         })));
59693         });
59694
59695         var rbush_1 = rbush;
59696         var _default$2 = rbush;
59697
59698
59699
59700         function rbush(maxEntries, format) {
59701             if (!(this instanceof rbush)) { return new rbush(maxEntries, format); }
59702
59703             // max entries in a node is 9 by default; min node fill is 40% for best performance
59704             this._maxEntries = Math.max(4, maxEntries || 9);
59705             this._minEntries = Math.max(2, Math.ceil(this._maxEntries * 0.4));
59706
59707             if (format) {
59708                 this._initFormat(format);
59709             }
59710
59711             this.clear();
59712         }
59713
59714         rbush.prototype = {
59715
59716             all: function () {
59717                 return this._all(this.data, []);
59718             },
59719
59720             search: function (bbox) {
59721
59722                 var node = this.data,
59723                     result = [],
59724                     toBBox = this.toBBox;
59725
59726                 if (!intersects$1(bbox, node)) { return result; }
59727
59728                 var nodesToSearch = [],
59729                     i, len, child, childBBox;
59730
59731                 while (node) {
59732                     for (i = 0, len = node.children.length; i < len; i++) {
59733
59734                         child = node.children[i];
59735                         childBBox = node.leaf ? toBBox(child) : child;
59736
59737                         if (intersects$1(bbox, childBBox)) {
59738                             if (node.leaf) { result.push(child); }
59739                             else if (contains$2(bbox, childBBox)) { this._all(child, result); }
59740                             else { nodesToSearch.push(child); }
59741                         }
59742                     }
59743                     node = nodesToSearch.pop();
59744                 }
59745
59746                 return result;
59747             },
59748
59749             collides: function (bbox) {
59750
59751                 var node = this.data,
59752                     toBBox = this.toBBox;
59753
59754                 if (!intersects$1(bbox, node)) { return false; }
59755
59756                 var nodesToSearch = [],
59757                     i, len, child, childBBox;
59758
59759                 while (node) {
59760                     for (i = 0, len = node.children.length; i < len; i++) {
59761
59762                         child = node.children[i];
59763                         childBBox = node.leaf ? toBBox(child) : child;
59764
59765                         if (intersects$1(bbox, childBBox)) {
59766                             if (node.leaf || contains$2(bbox, childBBox)) { return true; }
59767                             nodesToSearch.push(child);
59768                         }
59769                     }
59770                     node = nodesToSearch.pop();
59771                 }
59772
59773                 return false;
59774             },
59775
59776             load: function (data) {
59777                 if (!(data && data.length)) { return this; }
59778
59779                 if (data.length < this._minEntries) {
59780                     for (var i = 0, len = data.length; i < len; i++) {
59781                         this.insert(data[i]);
59782                     }
59783                     return this;
59784                 }
59785
59786                 // recursively build the tree with the given data from scratch using OMT algorithm
59787                 var node = this._build(data.slice(), 0, data.length - 1, 0);
59788
59789                 if (!this.data.children.length) {
59790                     // save as is if tree is empty
59791                     this.data = node;
59792
59793                 } else if (this.data.height === node.height) {
59794                     // split root if trees have the same height
59795                     this._splitRoot(this.data, node);
59796
59797                 } else {
59798                     if (this.data.height < node.height) {
59799                         // swap trees if inserted one is bigger
59800                         var tmpNode = this.data;
59801                         this.data = node;
59802                         node = tmpNode;
59803                     }
59804
59805                     // insert the small tree into the large tree at appropriate level
59806                     this._insert(node, this.data.height - node.height - 1, true);
59807                 }
59808
59809                 return this;
59810             },
59811
59812             insert: function (item) {
59813                 if (item) { this._insert(item, this.data.height - 1); }
59814                 return this;
59815             },
59816
59817             clear: function () {
59818                 this.data = createNode$1([]);
59819                 return this;
59820             },
59821
59822             remove: function (item, equalsFn) {
59823                 if (!item) { return this; }
59824
59825                 var node = this.data,
59826                     bbox = this.toBBox(item),
59827                     path = [],
59828                     indexes = [],
59829                     i, parent, index, goingUp;
59830
59831                 // depth-first iterative tree traversal
59832                 while (node || path.length) {
59833
59834                     if (!node) { // go up
59835                         node = path.pop();
59836                         parent = path[path.length - 1];
59837                         i = indexes.pop();
59838                         goingUp = true;
59839                     }
59840
59841                     if (node.leaf) { // check current node
59842                         index = findItem$1(item, node.children, equalsFn);
59843
59844                         if (index !== -1) {
59845                             // item found, remove the item and condense tree upwards
59846                             node.children.splice(index, 1);
59847                             path.push(node);
59848                             this._condense(path);
59849                             return this;
59850                         }
59851                     }
59852
59853                     if (!goingUp && !node.leaf && contains$2(node, bbox)) { // go down
59854                         path.push(node);
59855                         indexes.push(i);
59856                         i = 0;
59857                         parent = node;
59858                         node = node.children[0];
59859
59860                     } else if (parent) { // go right
59861                         i++;
59862                         node = parent.children[i];
59863                         goingUp = false;
59864
59865                     } else { node = null; } // nothing found
59866                 }
59867
59868                 return this;
59869             },
59870
59871             toBBox: function (item) { return item; },
59872
59873             compareMinX: compareNodeMinX$1,
59874             compareMinY: compareNodeMinY$1,
59875
59876             toJSON: function () { return this.data; },
59877
59878             fromJSON: function (data) {
59879                 this.data = data;
59880                 return this;
59881             },
59882
59883             _all: function (node, result) {
59884                 var nodesToSearch = [];
59885                 while (node) {
59886                     if (node.leaf) { result.push.apply(result, node.children); }
59887                     else { nodesToSearch.push.apply(nodesToSearch, node.children); }
59888
59889                     node = nodesToSearch.pop();
59890                 }
59891                 return result;
59892             },
59893
59894             _build: function (items, left, right, height) {
59895
59896                 var N = right - left + 1,
59897                     M = this._maxEntries,
59898                     node;
59899
59900                 if (N <= M) {
59901                     // reached leaf level; return leaf
59902                     node = createNode$1(items.slice(left, right + 1));
59903                     calcBBox$1(node, this.toBBox);
59904                     return node;
59905                 }
59906
59907                 if (!height) {
59908                     // target height of the bulk-loaded tree
59909                     height = Math.ceil(Math.log(N) / Math.log(M));
59910
59911                     // target number of root entries to maximize storage utilization
59912                     M = Math.ceil(N / Math.pow(M, height - 1));
59913                 }
59914
59915                 node = createNode$1([]);
59916                 node.leaf = false;
59917                 node.height = height;
59918
59919                 // split the items into M mostly square tiles
59920
59921                 var N2 = Math.ceil(N / M),
59922                     N1 = N2 * Math.ceil(Math.sqrt(M)),
59923                     i, j, right2, right3;
59924
59925                 multiSelect$1(items, left, right, N1, this.compareMinX);
59926
59927                 for (i = left; i <= right; i += N1) {
59928
59929                     right2 = Math.min(i + N1 - 1, right);
59930
59931                     multiSelect$1(items, i, right2, N2, this.compareMinY);
59932
59933                     for (j = i; j <= right2; j += N2) {
59934
59935                         right3 = Math.min(j + N2 - 1, right2);
59936
59937                         // pack each entry recursively
59938                         node.children.push(this._build(items, j, right3, height - 1));
59939                     }
59940                 }
59941
59942                 calcBBox$1(node, this.toBBox);
59943
59944                 return node;
59945             },
59946
59947             _chooseSubtree: function (bbox, node, level, path) {
59948
59949                 var i, len, child, targetNode, area, enlargement, minArea, minEnlargement;
59950
59951                 while (true) {
59952                     path.push(node);
59953
59954                     if (node.leaf || path.length - 1 === level) { break; }
59955
59956                     minArea = minEnlargement = Infinity;
59957
59958                     for (i = 0, len = node.children.length; i < len; i++) {
59959                         child = node.children[i];
59960                         area = bboxArea$1(child);
59961                         enlargement = enlargedArea$1(bbox, child) - area;
59962
59963                         // choose entry with the least area enlargement
59964                         if (enlargement < minEnlargement) {
59965                             minEnlargement = enlargement;
59966                             minArea = area < minArea ? area : minArea;
59967                             targetNode = child;
59968
59969                         } else if (enlargement === minEnlargement) {
59970                             // otherwise choose one with the smallest area
59971                             if (area < minArea) {
59972                                 minArea = area;
59973                                 targetNode = child;
59974                             }
59975                         }
59976                     }
59977
59978                     node = targetNode || node.children[0];
59979                 }
59980
59981                 return node;
59982             },
59983
59984             _insert: function (item, level, isNode) {
59985
59986                 var toBBox = this.toBBox,
59987                     bbox = isNode ? item : toBBox(item),
59988                     insertPath = [];
59989
59990                 // find the best node for accommodating the item, saving all nodes along the path too
59991                 var node = this._chooseSubtree(bbox, this.data, level, insertPath);
59992
59993                 // put the item into the node
59994                 node.children.push(item);
59995                 extend$3(node, bbox);
59996
59997                 // split on node overflow; propagate upwards if necessary
59998                 while (level >= 0) {
59999                     if (insertPath[level].children.length > this._maxEntries) {
60000                         this._split(insertPath, level);
60001                         level--;
60002                     } else { break; }
60003                 }
60004
60005                 // adjust bboxes along the insertion path
60006                 this._adjustParentBBoxes(bbox, insertPath, level);
60007             },
60008
60009             // split overflowed node into two
60010             _split: function (insertPath, level) {
60011
60012                 var node = insertPath[level],
60013                     M = node.children.length,
60014                     m = this._minEntries;
60015
60016                 this._chooseSplitAxis(node, m, M);
60017
60018                 var splitIndex = this._chooseSplitIndex(node, m, M);
60019
60020                 var newNode = createNode$1(node.children.splice(splitIndex, node.children.length - splitIndex));
60021                 newNode.height = node.height;
60022                 newNode.leaf = node.leaf;
60023
60024                 calcBBox$1(node, this.toBBox);
60025                 calcBBox$1(newNode, this.toBBox);
60026
60027                 if (level) { insertPath[level - 1].children.push(newNode); }
60028                 else { this._splitRoot(node, newNode); }
60029             },
60030
60031             _splitRoot: function (node, newNode) {
60032                 // split root node
60033                 this.data = createNode$1([node, newNode]);
60034                 this.data.height = node.height + 1;
60035                 this.data.leaf = false;
60036                 calcBBox$1(this.data, this.toBBox);
60037             },
60038
60039             _chooseSplitIndex: function (node, m, M) {
60040
60041                 var i, bbox1, bbox2, overlap, area, minOverlap, minArea, index;
60042
60043                 minOverlap = minArea = Infinity;
60044
60045                 for (i = m; i <= M - m; i++) {
60046                     bbox1 = distBBox$1(node, 0, i, this.toBBox);
60047                     bbox2 = distBBox$1(node, i, M, this.toBBox);
60048
60049                     overlap = intersectionArea$1(bbox1, bbox2);
60050                     area = bboxArea$1(bbox1) + bboxArea$1(bbox2);
60051
60052                     // choose distribution with minimum overlap
60053                     if (overlap < minOverlap) {
60054                         minOverlap = overlap;
60055                         index = i;
60056
60057                         minArea = area < minArea ? area : minArea;
60058
60059                     } else if (overlap === minOverlap) {
60060                         // otherwise choose distribution with minimum area
60061                         if (area < minArea) {
60062                             minArea = area;
60063                             index = i;
60064                         }
60065                     }
60066                 }
60067
60068                 return index;
60069             },
60070
60071             // sorts node children by the best axis for split
60072             _chooseSplitAxis: function (node, m, M) {
60073
60074                 var compareMinX = node.leaf ? this.compareMinX : compareNodeMinX$1,
60075                     compareMinY = node.leaf ? this.compareMinY : compareNodeMinY$1,
60076                     xMargin = this._allDistMargin(node, m, M, compareMinX),
60077                     yMargin = this._allDistMargin(node, m, M, compareMinY);
60078
60079                 // if total distributions margin value is minimal for x, sort by minX,
60080                 // otherwise it's already sorted by minY
60081                 if (xMargin < yMargin) { node.children.sort(compareMinX); }
60082             },
60083
60084             // total margin of all possible split distributions where each node is at least m full
60085             _allDistMargin: function (node, m, M, compare) {
60086
60087                 node.children.sort(compare);
60088
60089                 var toBBox = this.toBBox,
60090                     leftBBox = distBBox$1(node, 0, m, toBBox),
60091                     rightBBox = distBBox$1(node, M - m, M, toBBox),
60092                     margin = bboxMargin$1(leftBBox) + bboxMargin$1(rightBBox),
60093                     i, child;
60094
60095                 for (i = m; i < M - m; i++) {
60096                     child = node.children[i];
60097                     extend$3(leftBBox, node.leaf ? toBBox(child) : child);
60098                     margin += bboxMargin$1(leftBBox);
60099                 }
60100
60101                 for (i = M - m - 1; i >= m; i--) {
60102                     child = node.children[i];
60103                     extend$3(rightBBox, node.leaf ? toBBox(child) : child);
60104                     margin += bboxMargin$1(rightBBox);
60105                 }
60106
60107                 return margin;
60108             },
60109
60110             _adjustParentBBoxes: function (bbox, path, level) {
60111                 // adjust bboxes along the given tree path
60112                 for (var i = level; i >= 0; i--) {
60113                     extend$3(path[i], bbox);
60114                 }
60115             },
60116
60117             _condense: function (path) {
60118                 // go through the path, removing empty nodes and updating bboxes
60119                 for (var i = path.length - 1, siblings; i >= 0; i--) {
60120                     if (path[i].children.length === 0) {
60121                         if (i > 0) {
60122                             siblings = path[i - 1].children;
60123                             siblings.splice(siblings.indexOf(path[i]), 1);
60124
60125                         } else { this.clear(); }
60126
60127                     } else { calcBBox$1(path[i], this.toBBox); }
60128                 }
60129             },
60130
60131             _initFormat: function (format) {
60132                 // data format (minX, minY, maxX, maxY accessors)
60133
60134                 // uses eval-type function compilation instead of just accepting a toBBox function
60135                 // because the algorithms are very sensitive to sorting functions performance,
60136                 // so they should be dead simple and without inner calls
60137
60138                 var compareArr = ['return a', ' - b', ';'];
60139
60140                 this.compareMinX = new Function('a', 'b', compareArr.join(format[0]));
60141                 this.compareMinY = new Function('a', 'b', compareArr.join(format[1]));
60142
60143                 this.toBBox = new Function('a',
60144                     'return {minX: a' + format[0] +
60145                     ', minY: a' + format[1] +
60146                     ', maxX: a' + format[2] +
60147                     ', maxY: a' + format[3] + '};');
60148             }
60149         };
60150
60151         function findItem$1(item, items, equalsFn) {
60152             if (!equalsFn) { return items.indexOf(item); }
60153
60154             for (var i = 0; i < items.length; i++) {
60155                 if (equalsFn(item, items[i])) { return i; }
60156             }
60157             return -1;
60158         }
60159
60160         // calculate node's bbox from bboxes of its children
60161         function calcBBox$1(node, toBBox) {
60162             distBBox$1(node, 0, node.children.length, toBBox, node);
60163         }
60164
60165         // min bounding rectangle of node children from k to p-1
60166         function distBBox$1(node, k, p, toBBox, destNode) {
60167             if (!destNode) { destNode = createNode$1(null); }
60168             destNode.minX = Infinity;
60169             destNode.minY = Infinity;
60170             destNode.maxX = -Infinity;
60171             destNode.maxY = -Infinity;
60172
60173             for (var i = k, child; i < p; i++) {
60174                 child = node.children[i];
60175                 extend$3(destNode, node.leaf ? toBBox(child) : child);
60176             }
60177
60178             return destNode;
60179         }
60180
60181         function extend$3(a, b) {
60182             a.minX = Math.min(a.minX, b.minX);
60183             a.minY = Math.min(a.minY, b.minY);
60184             a.maxX = Math.max(a.maxX, b.maxX);
60185             a.maxY = Math.max(a.maxY, b.maxY);
60186             return a;
60187         }
60188
60189         function compareNodeMinX$1(a, b) { return a.minX - b.minX; }
60190         function compareNodeMinY$1(a, b) { return a.minY - b.minY; }
60191
60192         function bboxArea$1(a)   { return (a.maxX - a.minX) * (a.maxY - a.minY); }
60193         function bboxMargin$1(a) { return (a.maxX - a.minX) + (a.maxY - a.minY); }
60194
60195         function enlargedArea$1(a, b) {
60196             return (Math.max(b.maxX, a.maxX) - Math.min(b.minX, a.minX)) *
60197                    (Math.max(b.maxY, a.maxY) - Math.min(b.minY, a.minY));
60198         }
60199
60200         function intersectionArea$1(a, b) {
60201             var minX = Math.max(a.minX, b.minX),
60202                 minY = Math.max(a.minY, b.minY),
60203                 maxX = Math.min(a.maxX, b.maxX),
60204                 maxY = Math.min(a.maxY, b.maxY);
60205
60206             return Math.max(0, maxX - minX) *
60207                    Math.max(0, maxY - minY);
60208         }
60209
60210         function contains$2(a, b) {
60211             return a.minX <= b.minX &&
60212                    a.minY <= b.minY &&
60213                    b.maxX <= a.maxX &&
60214                    b.maxY <= a.maxY;
60215         }
60216
60217         function intersects$1(a, b) {
60218             return b.minX <= a.maxX &&
60219                    b.minY <= a.maxY &&
60220                    b.maxX >= a.minX &&
60221                    b.maxY >= a.minY;
60222         }
60223
60224         function createNode$1(children) {
60225             return {
60226                 children: children,
60227                 height: 1,
60228                 leaf: true,
60229                 minX: Infinity,
60230                 minY: Infinity,
60231                 maxX: -Infinity,
60232                 maxY: -Infinity
60233             };
60234         }
60235
60236         // sort an array so that items come in groups of n unsorted items, with groups sorted between each other;
60237         // combines selection algorithm with binary divide & conquer approach
60238
60239         function multiSelect$1(arr, left, right, n, compare) {
60240             var stack = [left, right],
60241                 mid;
60242
60243             while (stack.length) {
60244                 right = stack.pop();
60245                 left = stack.pop();
60246
60247                 if (right - left <= n) { continue; }
60248
60249                 mid = left + Math.ceil((right - left) / n / 2) * n;
60250                 quickselect$1(arr, mid, left, right, compare);
60251
60252                 stack.push(left, mid, mid, right);
60253             }
60254         }
60255         rbush_1.default = _default$2;
60256
60257         var lineclip_1$1 = lineclip$1;
60258
60259         lineclip$1.polyline = lineclip$1;
60260         lineclip$1.polygon = polygonclip$1;
60261
60262
60263         // Cohen-Sutherland line clippign algorithm, adapted to efficiently
60264         // handle polylines rather than just segments
60265
60266         function lineclip$1(points, bbox, result) {
60267
60268             var len = points.length,
60269                 codeA = bitCode$1(points[0], bbox),
60270                 part = [],
60271                 i, a, b, codeB, lastCode;
60272
60273             if (!result) { result = []; }
60274
60275             for (i = 1; i < len; i++) {
60276                 a = points[i - 1];
60277                 b = points[i];
60278                 codeB = lastCode = bitCode$1(b, bbox);
60279
60280                 while (true) {
60281
60282                     if (!(codeA | codeB)) { // accept
60283                         part.push(a);
60284
60285                         if (codeB !== lastCode) { // segment went outside
60286                             part.push(b);
60287
60288                             if (i < len - 1) { // start a new line
60289                                 result.push(part);
60290                                 part = [];
60291                             }
60292                         } else if (i === len - 1) {
60293                             part.push(b);
60294                         }
60295                         break;
60296
60297                     } else if (codeA & codeB) { // trivial reject
60298                         break;
60299
60300                     } else if (codeA) { // a outside, intersect with clip edge
60301                         a = intersect$1(a, b, codeA, bbox);
60302                         codeA = bitCode$1(a, bbox);
60303
60304                     } else { // b outside
60305                         b = intersect$1(a, b, codeB, bbox);
60306                         codeB = bitCode$1(b, bbox);
60307                     }
60308                 }
60309
60310                 codeA = lastCode;
60311             }
60312
60313             if (part.length) { result.push(part); }
60314
60315             return result;
60316         }
60317
60318         // Sutherland-Hodgeman polygon clipping algorithm
60319
60320         function polygonclip$1(points, bbox) {
60321
60322             var result, edge, prev, prevInside, i, p, inside;
60323
60324             // clip against each side of the clip rectangle
60325             for (edge = 1; edge <= 8; edge *= 2) {
60326                 result = [];
60327                 prev = points[points.length - 1];
60328                 prevInside = !(bitCode$1(prev, bbox) & edge);
60329
60330                 for (i = 0; i < points.length; i++) {
60331                     p = points[i];
60332                     inside = !(bitCode$1(p, bbox) & edge);
60333
60334                     // if segment goes through the clip window, add an intersection
60335                     if (inside !== prevInside) { result.push(intersect$1(prev, p, edge, bbox)); }
60336
60337                     if (inside) { result.push(p); } // add a point if it's inside
60338
60339                     prev = p;
60340                     prevInside = inside;
60341                 }
60342
60343                 points = result;
60344
60345                 if (!points.length) { break; }
60346             }
60347
60348             return result;
60349         }
60350
60351         // intersect a segment against one of the 4 lines that make up the bbox
60352
60353         function intersect$1(a, b, edge, bbox) {
60354             return edge & 8 ? [a[0] + (b[0] - a[0]) * (bbox[3] - a[1]) / (b[1] - a[1]), bbox[3]] : // top
60355                    edge & 4 ? [a[0] + (b[0] - a[0]) * (bbox[1] - a[1]) / (b[1] - a[1]), bbox[1]] : // bottom
60356                    edge & 2 ? [bbox[2], a[1] + (b[1] - a[1]) * (bbox[2] - a[0]) / (b[0] - a[0])] : // right
60357                    edge & 1 ? [bbox[0], a[1] + (b[1] - a[1]) * (bbox[0] - a[0]) / (b[0] - a[0])] : // left
60358                    null;
60359         }
60360
60361         // bit code reflects the point position relative to the bbox:
60362
60363         //         left  mid  right
60364         //    top  1001  1000  1010
60365         //    mid  0001  0000  0010
60366         // bottom  0101  0100  0110
60367
60368         function bitCode$1(p, bbox) {
60369             var code = 0;
60370
60371             if (p[0] < bbox[0]) { code |= 1; } // left
60372             else if (p[0] > bbox[2]) { code |= 2; } // right
60373
60374             if (p[1] < bbox[1]) { code |= 4; } // bottom
60375             else if (p[1] > bbox[3]) { code |= 8; } // top
60376
60377             return code;
60378         }
60379
60380         var whichPolygon_1 = whichPolygon;
60381
60382         function whichPolygon(data) {
60383             var bboxes = [];
60384             for (var i = 0; i < data.features.length; i++) {
60385                 var feature = data.features[i];
60386                 var coords = feature.geometry.coordinates;
60387
60388                 if (feature.geometry.type === 'Polygon') {
60389                     bboxes.push(treeItem(coords, feature.properties));
60390
60391                 } else if (feature.geometry.type === 'MultiPolygon') {
60392                     for (var j = 0; j < coords.length; j++) {
60393                         bboxes.push(treeItem(coords[j], feature.properties));
60394                     }
60395                 }
60396             }
60397
60398             var tree = rbush_1().load(bboxes);
60399
60400             function query(p, multi) {
60401                 var output = [],
60402                     result = tree.search({
60403                         minX: p[0],
60404                         minY: p[1],
60405                         maxX: p[0],
60406                         maxY: p[1]
60407                     });
60408                 for (var i = 0; i < result.length; i++) {
60409                     if (insidePolygon(result[i].coords, p)) {
60410                         if (multi)
60411                             { output.push(result[i].props); }
60412                         else
60413                             { return result[i].props; }
60414                     }
60415                 }
60416                 return multi && output.length ? output : null;
60417             }
60418
60419             query.tree = tree;
60420             query.bbox = function queryBBox(bbox) {
60421                 var output = [];
60422                 var result = tree.search({
60423                     minX: bbox[0],
60424                     minY: bbox[1],
60425                     maxX: bbox[2],
60426                     maxY: bbox[3]
60427                 });
60428                 for (var i = 0; i < result.length; i++) {
60429                     if (polygonIntersectsBBox(result[i].coords, bbox)) {
60430                         output.push(result[i].props);
60431                     }
60432                 }
60433                 return output;
60434             };
60435
60436             return query;
60437         }
60438
60439         function polygonIntersectsBBox(polygon, bbox) {
60440             var bboxCenter = [
60441                 (bbox[0] + bbox[2]) / 2,
60442                 (bbox[1] + bbox[3]) / 2
60443             ];
60444             if (insidePolygon(polygon, bboxCenter)) { return true; }
60445             for (var i = 0; i < polygon.length; i++) {
60446                 if (lineclip_1$1(polygon[i], bbox).length > 0) { return true; }
60447             }
60448             return false;
60449         }
60450
60451         // ray casting algorithm for detecting if point is in polygon
60452         function insidePolygon(rings, p) {
60453             var inside = false;
60454             for (var i = 0, len = rings.length; i < len; i++) {
60455                 var ring = rings[i];
60456                 for (var j = 0, len2 = ring.length, k = len2 - 1; j < len2; k = j++) {
60457                     if (rayIntersect(p, ring[j], ring[k])) { inside = !inside; }
60458                 }
60459             }
60460             return inside;
60461         }
60462
60463         function rayIntersect(p, p1, p2) {
60464             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]);
60465         }
60466
60467         function treeItem(coords, props) {
60468             var item = {
60469                 minX: Infinity,
60470                 minY: Infinity,
60471                 maxX: -Infinity,
60472                 maxY: -Infinity,
60473                 coords: coords,
60474                 props: props
60475             };
60476
60477             for (var i = 0; i < coords[0].length; i++) {
60478                 var p = coords[0][i];
60479                 item.minX = Math.min(item.minX, p[0]);
60480                 item.minY = Math.min(item.minY, p[1]);
60481                 item.maxX = Math.max(item.maxX, p[0]);
60482                 item.maxY = Math.max(item.maxY, p[1]);
60483             }
60484             return item;
60485         }
60486
60487         var type = "FeatureCollection";
60488         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]]]]}}];
60489         var rawBorders = {
60490         type: type,
60491         features: features
60492         };
60493
60494         var borders = rawBorders;
60495         var whichPolygonGetter = {};
60496         var featuresByCode = {};
60497         var idFilterRegex = /\bThe\b|\bthe\b|\band\b|\bof\b|[-_ .,()&[\]/]/g;
60498         var levels = [
60499           'subterritory',
60500           'territory',
60501           'country',
60502           'intermediateRegion',
60503           'subregion',
60504           'region',
60505           'union',
60506           'world'
60507         ];
60508         loadDerivedDataAndCaches(borders);
60509         function loadDerivedDataAndCaches(borders) {
60510           var identifierProps = ['iso1A2', 'iso1A3', 'm49', 'wikidata', 'emojiFlag', 'nameEn'];
60511           var geometryFeatures = [];
60512           for (var i in borders.features) {
60513             var feature = borders.features[i];
60514             feature.properties.id = feature.properties.iso1A2 || feature.properties.m49;
60515             loadM49(feature);
60516             loadIsoStatus(feature);
60517             loadLevel(feature);
60518             loadGroups(feature);
60519             loadRoadSpeedUnit(feature);
60520             loadDriveSide(feature);
60521             loadFlag(feature);
60522             cacheFeatureByIDs(feature);
60523             if (feature.geometry) { geometryFeatures.push(feature); }
60524           }
60525           for (var i$1 in borders.features) {
60526             var feature$1 = borders.features[i$1];
60527             feature$1.properties.groups.sort(function(groupID1, groupID2) {
60528               return (
60529                 levels.indexOf(featuresByCode[groupID1].properties.level) -
60530                 levels.indexOf(featuresByCode[groupID2].properties.level)
60531               );
60532             });
60533             loadMembersForGroupsOf(feature$1);
60534           }
60535           var geometryOnlyCollection = {
60536             type: 'RegionFeatureCollection',
60537             features: geometryFeatures
60538           };
60539           whichPolygonGetter = whichPolygon_1(geometryOnlyCollection);
60540           function loadGroups(feature) {
60541             var props = feature.properties;
60542             if (!props.groups) {
60543               props.groups = [];
60544             }
60545             if (props.country) {
60546               props.groups.push(props.country);
60547             }
60548             if (props.m49 !== '001') {
60549               props.groups.push('001');
60550             }
60551           }
60552           function loadM49(feature) {
60553             var props = feature.properties;
60554             if (!props.m49 && props.iso1N3) {
60555               props.m49 = props.iso1N3;
60556             }
60557           }
60558           function loadIsoStatus(feature) {
60559             var props = feature.properties;
60560             if (!props.isoStatus && props.iso1A2) {
60561               props.isoStatus = 'official';
60562             }
60563           }
60564           function loadLevel(feature) {
60565             var props = feature.properties;
60566             if (props.level) { return; }
60567             if (!props.country) {
60568               props.level = 'country';
60569             } else if (props.isoStatus === 'official') {
60570               props.level = 'territory';
60571             } else {
60572               props.level = 'subterritory';
60573             }
60574           }
60575           function loadRoadSpeedUnit(feature) {
60576             var props = feature.properties;
60577             if (props.roadSpeedUnit === undefined && props.iso1A2 && props.iso1A2 !== 'EU') {
60578               props.roadSpeedUnit = 'km/h';
60579             }
60580           }
60581           function loadDriveSide(feature) {
60582             var props = feature.properties;
60583             if (props.driveSide === undefined && props.iso1A2 && props.iso1A2 !== 'EU') {
60584               props.driveSide = 'right';
60585             }
60586           }
60587           function loadFlag(feature) {
60588             if (!feature.properties.iso1A2) { return; }
60589             var flag = feature.properties.iso1A2.replace(/./g, function(char) {
60590               return String.fromCodePoint(char.charCodeAt(0) + 127397);
60591             });
60592             feature.properties.emojiFlag = flag;
60593           }
60594           function loadMembersForGroupsOf(feature) {
60595             var featureID = feature.properties.id;
60596             var standardizedGroupIDs = [];
60597             for (var j in feature.properties.groups) {
60598               var groupID = feature.properties.groups[j];
60599               var groupFeature = featuresByCode[groupID];
60600               standardizedGroupIDs.push(groupFeature.properties.id);
60601               if (groupFeature.properties.members) {
60602                 groupFeature.properties.members.push(featureID);
60603               } else {
60604                 groupFeature.properties.members = [featureID];
60605               }
60606             }
60607             feature.properties.groups = standardizedGroupIDs;
60608           }
60609           function cacheFeatureByIDs(feature) {
60610             for (var k in identifierProps) {
60611               var prop = identifierProps[k];
60612               var id = prop && feature.properties[prop];
60613               if (id) {
60614                 id = id.replace(idFilterRegex, '').toUpperCase();
60615                 featuresByCode[id] = feature;
60616               }
60617             }
60618             if (feature.properties.aliases) {
60619               for (var j in feature.properties.aliases) {
60620                 var alias = feature.properties.aliases[j].replace(idFilterRegex, '').toUpperCase();
60621                 featuresByCode[alias] = feature;
60622               }
60623             }
60624           }
60625         }
60626         function locArray(loc) {
60627           if (Array.isArray(loc)) {
60628             return loc;
60629           } else if (loc.coordinates) {
60630             return loc.coordinates;
60631           }
60632           return loc.geometry.coordinates;
60633         }
60634         function smallestFeature(loc) {
60635           var query = locArray(loc);
60636           var featureProperties = whichPolygonGetter(query);
60637           if (!featureProperties) { return null; }
60638           return featuresByCode[featureProperties.id];
60639         }
60640         function countryFeature(loc) {
60641           var feature = smallestFeature(loc);
60642           if (!feature) { return null; }
60643           var countryCode = feature.properties.country || feature.properties.iso1A2;
60644           return featuresByCode[countryCode];
60645         }
60646         function featureForLoc(loc, opts) {
60647           if (opts && opts.level && opts.level !== 'country') {
60648             var features = featuresContaining(loc);
60649             var targetLevel = opts.level;
60650             var targetLevelIndex = levels.indexOf(targetLevel);
60651             if (targetLevelIndex === -1) { return null; }
60652             for (var i in features) {
60653               var feature = features[i];
60654               if (
60655                 feature.properties.level === targetLevel ||
60656                 levels.indexOf(feature.properties.level) > targetLevelIndex
60657               ) {
60658                 return feature;
60659               }
60660             }
60661             return null;
60662           }
60663           return countryFeature(loc);
60664         }
60665         function featureForID(id) {
60666           var stringID;
60667           if (typeof id === 'number') {
60668             stringID = id.toString();
60669             if (stringID.length === 1) {
60670               stringID = '00' + stringID;
60671             } else if (stringID.length === 2) {
60672               stringID = '0' + stringID;
60673             }
60674           } else {
60675             stringID = id.replace(idFilterRegex, '').toUpperCase();
60676           }
60677           return featuresByCode[stringID] || null;
60678         }
60679         function smallestOrMatchingFeature(query) {
60680           if (typeof query === 'object') {
60681             return smallestFeature(query);
60682           }
60683           return featureForID(query);
60684         }
60685         function feature(query, opts) {
60686           if (typeof query === 'object') {
60687             return featureForLoc(query, opts);
60688           }
60689           return featureForID(query);
60690         }
60691         function iso1A2Code(query, opts) {
60692           var match = feature(query, opts);
60693           if (!match) { return null; }
60694           return match.properties.iso1A2 || null;
60695         }
60696         function featuresContaining(query, strict) {
60697           var feature = smallestOrMatchingFeature(query);
60698           if (!feature) { return []; }
60699           var features = [];
60700           if (!strict || typeof query === 'object') {
60701             features.push(feature);
60702           }
60703           var properties = feature.properties;
60704           for (var i in properties.groups) {
60705             var groupID = properties.groups[i];
60706             features.push(featuresByCode[groupID]);
60707           }
60708           return features;
60709         }
60710         function roadSpeedUnit(query) {
60711           var feature = smallestOrMatchingFeature(query);
60712           return (feature && feature.properties.roadSpeedUnit) || null;
60713         }
60714
60715         var _dataDeprecated;
60716         var _nsi;
60717
60718         function validationOutdatedTags() {
60719           var type = 'outdated_tags';
60720           var nsiKeys = ['amenity', 'shop', 'tourism', 'leisure', 'office'];
60721
60722           // A concern here in switching to async data means that `_dataDeprecated`
60723           // and `_nsi` will not be available at first, so the data on early tiles
60724           // may not have tags validated fully.
60725
60726           // initialize deprecated tags array
60727           _mainFileFetcher.get('deprecated')
60728             .then(function (d) { return _dataDeprecated = d; })
60729             .catch(function () { /* ignore */ });
60730
60731           _mainFileFetcher.get('nsi_brands')
60732             .then(function (d) {
60733               _nsi = {
60734                 brands: d.brands,
60735                 matcher: matcher$1(),
60736                 wikidata: {},
60737                 wikipedia: {}
60738               };
60739
60740               // initialize name-suggestion-index matcher
60741               _nsi.matcher.buildMatchIndex(d.brands);
60742
60743               // index all known wikipedia and wikidata tags
60744               Object.keys(d.brands).forEach(function (kvnd) {
60745                 var brand = d.brands[kvnd];
60746                 var wd = brand.tags['brand:wikidata'];
60747                 var wp = brand.tags['brand:wikipedia'];
60748                 if (wd) { _nsi.wikidata[wd] = kvnd; }
60749                 if (wp) { _nsi.wikipedia[wp] = kvnd; }
60750               });
60751
60752               return _nsi;
60753             })
60754             .catch(function () { /* ignore */ });
60755
60756
60757           function oldTagIssues(entity, graph) {
60758             var oldTags = Object.assign({}, entity.tags);  // shallow copy
60759             var preset = _mainPresetIndex.match(entity, graph);
60760             var subtype = 'deprecated_tags';
60761             if (!preset) { return []; }
60762
60763             // upgrade preset..
60764             if (preset.replacement) {
60765               var newPreset = _mainPresetIndex.item(preset.replacement);
60766               graph = actionChangePreset(entity.id, preset, newPreset, true /* skip field defaults */)(graph);
60767               entity = graph.entity(entity.id);
60768               preset = newPreset;
60769             }
60770
60771             // upgrade tags..
60772             if (_dataDeprecated) {
60773               var deprecatedTags = entity.deprecatedTags(_dataDeprecated);
60774               if (deprecatedTags.length) {
60775                 deprecatedTags.forEach(function (tag) {
60776                   graph = actionUpgradeTags(entity.id, tag.old, tag.replace)(graph);
60777                 });
60778                 entity = graph.entity(entity.id);
60779               }
60780             }
60781
60782             // add missing addTags..
60783             var newTags = Object.assign({}, entity.tags);  // shallow copy
60784             if (preset.tags !== preset.addTags) {
60785               Object.keys(preset.addTags).forEach(function (k) {
60786                 if (!newTags[k]) {
60787                   if (preset.addTags[k] === '*') {
60788                     newTags[k] = 'yes';
60789                   } else {
60790                     newTags[k] = preset.addTags[k];
60791                   }
60792                 }
60793               });
60794             }
60795
60796             if (_nsi) {
60797               // Do `wikidata` or `wikipedia` identify this entity as a brand?  #6416
60798               // If so, these tags can be swapped to `brand:wikidata`/`brand:wikipedia`
60799               var isBrand;
60800               if (newTags.wikidata) {                 // try matching `wikidata`
60801                 isBrand = _nsi.wikidata[newTags.wikidata];
60802               }
60803               if (!isBrand && newTags.wikipedia) {    // fallback to `wikipedia`
60804                 isBrand = _nsi.wikipedia[newTags.wikipedia];
60805               }
60806               if (isBrand && !newTags.office) {       // but avoid doing this for corporate offices
60807                 if (newTags.wikidata) {
60808                   newTags['brand:wikidata'] = newTags.wikidata;
60809                   delete newTags.wikidata;
60810                 }
60811                 if (newTags.wikipedia) {
60812                   newTags['brand:wikipedia'] = newTags.wikipedia;
60813                   delete newTags.wikipedia;
60814                 }
60815                 // I considered setting `name` and other tags here, but they aren't unique per wikidata
60816                 // (Q2759586 -> in USA "Papa John's", in Russia "Папа Джонс")
60817                 // So users will really need to use a preset or assign `name` themselves.
60818               }
60819
60820               // try key/value|name match against name-suggestion-index
60821               if (newTags.name) {
60822                 for (var i = 0; i < nsiKeys.length; i++) {
60823                   var k = nsiKeys[i];
60824                   if (!newTags[k]) { continue; }
60825
60826                   var center = entity.extent(graph).center();
60827                   var countryCode = iso1A2Code(center);
60828                   var match = _nsi.matcher.matchKVN(k, newTags[k], newTags.name, countryCode && countryCode.toLowerCase());
60829                   if (!match) { continue; }
60830
60831                   // for now skip ambiguous matches (like Target~(USA) vs Target~(Australia))
60832                   if (match.d) { continue; }
60833
60834                   var brand = _nsi.brands[match.kvnd];
60835                   if (brand && brand.tags['brand:wikidata'] &&
60836                     brand.tags['brand:wikidata'] !== entity.tags['not:brand:wikidata']) {
60837                     subtype = 'noncanonical_brand';
60838
60839                     var keepTags = ['takeaway'].reduce(function (acc, k) {
60840                       if (newTags[k]) {
60841                         acc[k] = newTags[k];
60842                       }
60843                       return acc;
60844                     }, {});
60845
60846                     nsiKeys.forEach(function (k) { return delete newTags[k]; });
60847                     Object.assign(newTags, brand.tags, keepTags);
60848                     break;
60849                   }
60850                 }
60851               }
60852             }
60853
60854             // determine diff
60855             var tagDiff = utilTagDiff(oldTags, newTags);
60856             if (!tagDiff.length) { return []; }
60857
60858             var isOnlyAddingTags = tagDiff.every(function (d) { return d.type === '+'; });
60859
60860             var prefix = '';
60861             if (subtype === 'noncanonical_brand') {
60862               prefix = 'noncanonical_brand.';
60863             } else if (subtype === 'deprecated_tags' && isOnlyAddingTags) {
60864               subtype = 'incomplete_tags';
60865               prefix = 'incomplete.';
60866             }
60867
60868             // don't allow autofixing brand tags
60869             var autoArgs = subtype !== 'noncanonical_brand' ? [doUpgrade, _t('issues.fix.upgrade_tags.annotation')] : null;
60870
60871             return [new validationIssue({
60872               type: type,
60873               subtype: subtype,
60874               severity: 'warning',
60875               message: showMessage,
60876               reference: showReference,
60877               entityIds: [entity.id],
60878               hash: JSON.stringify(tagDiff),
60879               dynamicFixes: function () {
60880                 return [
60881                   new validationIssueFix({
60882                     autoArgs: autoArgs,
60883                     title: _t('issues.fix.upgrade_tags.title'),
60884                     onClick: function (context) {
60885                       context.perform(doUpgrade, _t('issues.fix.upgrade_tags.annotation'));
60886                     }
60887                   })
60888                 ];
60889               }
60890             })];
60891
60892
60893             function doUpgrade(graph) {
60894               var currEntity = graph.hasEntity(entity.id);
60895               if (!currEntity) { return graph; }
60896
60897               var newTags = Object.assign({}, currEntity.tags);  // shallow copy
60898               tagDiff.forEach(function (diff) {
60899                 if (diff.type === '-') {
60900                   delete newTags[diff.key];
60901                 } else if (diff.type === '+') {
60902                   newTags[diff.key] = diff.newVal;
60903                 }
60904               });
60905
60906               return actionChangeTags(currEntity.id, newTags)(graph);
60907             }
60908
60909
60910             function showMessage(context) {
60911               var currEntity = context.hasEntity(entity.id);
60912               if (!currEntity) { return ''; }
60913
60914               var messageID = "issues.outdated_tags." + prefix + "message";
60915               if (subtype === 'noncanonical_brand' && isOnlyAddingTags) {
60916                 messageID += '_incomplete';
60917               }
60918               return _t(messageID, { feature: utilDisplayLabel(currEntity, context.graph()) });
60919             }
60920
60921
60922             function showReference(selection) {
60923               var enter = selection.selectAll('.issue-reference')
60924                 .data([0])
60925                 .enter();
60926
60927               enter
60928                 .append('div')
60929                 .attr('class', 'issue-reference')
60930                 .text(_t(("issues.outdated_tags." + prefix + "reference")));
60931
60932               enter
60933                 .append('strong')
60934                 .text(_t('issues.suggested'));
60935
60936               enter
60937                 .append('table')
60938                 .attr('class', 'tagDiff-table')
60939                 .selectAll('.tagDiff-row')
60940                 .data(tagDiff)
60941                 .enter()
60942                 .append('tr')
60943                 .attr('class', 'tagDiff-row')
60944                 .append('td')
60945                 .attr('class', function (d) {
60946                   var klass = d.type === '+' ? 'add' : 'remove';
60947                   return ("tagDiff-cell tagDiff-cell-" + klass);
60948                 })
60949                 .text(function (d) { return d.display; });
60950             }
60951           }
60952
60953
60954           function oldMultipolygonIssues(entity, graph) {
60955             var multipolygon, outerWay;
60956             if (entity.type === 'relation') {
60957               outerWay = osmOldMultipolygonOuterMemberOfRelation(entity, graph);
60958               multipolygon = entity;
60959             } else if (entity.type === 'way') {
60960               multipolygon = osmIsOldMultipolygonOuterMember(entity, graph);
60961               outerWay = entity;
60962             } else {
60963               return [];
60964             }
60965
60966             if (!multipolygon || !outerWay) { return []; }
60967
60968             return [new validationIssue({
60969               type: type,
60970               subtype: 'old_multipolygon',
60971               severity: 'warning',
60972               message: showMessage,
60973               reference: showReference,
60974               entityIds: [outerWay.id, multipolygon.id],
60975               dynamicFixes: function () {
60976                 return [
60977                   new validationIssueFix({
60978                     autoArgs: [doUpgrade, _t('issues.fix.move_tags.annotation')],
60979                     title: _t('issues.fix.move_tags.title'),
60980                     onClick: function (context) {
60981                       context.perform(doUpgrade, _t('issues.fix.move_tags.annotation'));
60982                     }
60983                   })
60984                 ];
60985               }
60986             })];
60987
60988
60989             function doUpgrade(graph) {
60990               var currMultipolygon = graph.hasEntity(multipolygon.id);
60991               var currOuterWay = graph.hasEntity(outerWay.id);
60992               if (!currMultipolygon || !currOuterWay) { return graph; }
60993
60994               currMultipolygon = currMultipolygon.mergeTags(currOuterWay.tags);
60995               graph = graph.replace(currMultipolygon);
60996               return actionChangeTags(currOuterWay.id, {})(graph);
60997             }
60998
60999
61000             function showMessage(context) {
61001               var currMultipolygon = context.hasEntity(multipolygon.id);
61002               if (!currMultipolygon) { return ''; }
61003
61004               return _t('issues.old_multipolygon.message',
61005                   { multipolygon: utilDisplayLabel(currMultipolygon, context.graph()) }
61006               );
61007             }
61008
61009
61010             function showReference(selection) {
61011               selection.selectAll('.issue-reference')
61012                 .data([0])
61013                 .enter()
61014                 .append('div')
61015                 .attr('class', 'issue-reference')
61016                 .text(_t('issues.old_multipolygon.reference'));
61017             }
61018           }
61019
61020
61021           var validation = function checkOutdatedTags(entity, graph) {
61022             var issues = oldMultipolygonIssues(entity, graph);
61023             if (!issues.length) { issues = oldTagIssues(entity, graph); }
61024             return issues;
61025           };
61026
61027
61028           validation.type = type;
61029
61030           return validation;
61031         }
61032
61033         function validationPrivateData() {
61034             var type = 'private_data';
61035
61036             // assume that some buildings are private
61037             var privateBuildingValues = {
61038                 detached: true,
61039                 farm: true,
61040                 house: true,
61041                 houseboat: true,
61042                 residential: true,
61043                 semidetached_house: true,
61044                 static_caravan: true
61045             };
61046
61047             // but they might be public if they have one of these other tags
61048             var publicKeys = {
61049                 amenity: true,
61050                 craft: true,
61051                 historic: true,
61052                 leisure: true,
61053                 office: true,
61054                 shop: true,
61055                 tourism: true
61056             };
61057
61058             // these tags may contain personally identifying info
61059             var personalTags = {
61060                 'contact:email': true,
61061                 'contact:fax': true,
61062                 'contact:phone': true,
61063                 email: true,
61064                 fax: true,
61065                 phone: true
61066             };
61067
61068
61069             var validation = function checkPrivateData(entity) {
61070                 var tags = entity.tags;
61071                 if (!tags.building || !privateBuildingValues[tags.building]) { return []; }
61072
61073                 var keepTags = {};
61074                 for (var k in tags) {
61075                     if (publicKeys[k]) { return []; }  // probably a public feature
61076                     if (!personalTags[k]) {
61077                         keepTags[k] = tags[k];
61078                     }
61079                 }
61080
61081                 var tagDiff = utilTagDiff(tags, keepTags);
61082                 if (!tagDiff.length) { return []; }
61083
61084                 var fixID = tagDiff.length === 1 ? 'remove_tag' : 'remove_tags';
61085
61086                 return [new validationIssue({
61087                     type: type,
61088                     severity: 'warning',
61089                     message: showMessage,
61090                     reference: showReference,
61091                     entityIds: [entity.id],
61092                     dynamicFixes: function() {
61093                         return [
61094                             new validationIssueFix({
61095                                 icon: 'iD-operation-delete',
61096                                 title: _t('issues.fix.' + fixID + '.title'),
61097                                 onClick: function(context) {
61098                                     context.perform(doUpgrade, _t('issues.fix.upgrade_tags.annotation'));
61099                                 }
61100                             })
61101                         ];
61102                     }
61103                 })];
61104
61105
61106                 function doUpgrade(graph) {
61107                     var currEntity = graph.hasEntity(entity.id);
61108                     if (!currEntity) { return graph; }
61109
61110                     var newTags = Object.assign({}, currEntity.tags);  // shallow copy
61111                     tagDiff.forEach(function(diff) {
61112                         if (diff.type === '-') {
61113                             delete newTags[diff.key];
61114                         } else if (diff.type === '+') {
61115                             newTags[diff.key] = diff.newVal;
61116                         }
61117                     });
61118
61119                     return actionChangeTags(currEntity.id, newTags)(graph);
61120                 }
61121
61122
61123                 function showMessage(context) {
61124                     var currEntity = context.hasEntity(this.entityIds[0]);
61125                     if (!currEntity) { return ''; }
61126
61127                     return _t('issues.private_data.contact.message',
61128                         { feature: utilDisplayLabel(currEntity, context.graph()) }
61129                     );
61130                 }
61131
61132
61133                 function showReference(selection) {
61134                     var enter = selection.selectAll('.issue-reference')
61135                         .data([0])
61136                         .enter();
61137
61138                     enter
61139                         .append('div')
61140                         .attr('class', 'issue-reference')
61141                         .text(_t('issues.private_data.reference'));
61142
61143                     enter
61144                         .append('strong')
61145                         .text(_t('issues.suggested'));
61146
61147                     enter
61148                         .append('table')
61149                         .attr('class', 'tagDiff-table')
61150                         .selectAll('.tagDiff-row')
61151                         .data(tagDiff)
61152                         .enter()
61153                         .append('tr')
61154                         .attr('class', 'tagDiff-row')
61155                         .append('td')
61156                         .attr('class', function(d) {
61157                             var klass = d.type === '+' ? 'add' : 'remove';
61158                             return 'tagDiff-cell tagDiff-cell-' + klass;
61159                         })
61160                         .text(function(d) { return d.display; });
61161                 }
61162             };
61163
61164
61165             validation.type = type;
61166
61167             return validation;
61168         }
61169
61170         var _discardNameRegexes = [];
61171
61172         function validationSuspiciousName() {
61173           var type = 'suspicious_name';
61174           var keysToTestForGenericValues = [
61175             'aerialway', 'aeroway', 'amenity', 'building', 'craft', 'highway',
61176             'leisure', 'railway', 'man_made', 'office', 'shop', 'tourism', 'waterway'
61177           ];
61178
61179           // A concern here in switching to async data means that `_nsiFilters` will not
61180           // be available at first, so the data on early tiles may not have tags validated fully.
61181
61182           _mainFileFetcher.get('nsi_filters')
61183             .then(function (filters) {
61184               // known list of generic names (e.g. "bar")
61185               _discardNameRegexes = filters.discardNames
61186                 .map(function (discardName) { return new RegExp(discardName, 'i'); });
61187             })
61188             .catch(function () { /* ignore */ });
61189
61190
61191           function isDiscardedSuggestionName(lowercaseName) {
61192             return _discardNameRegexes.some(function (regex) { return regex.test(lowercaseName); });
61193           }
61194
61195           // test if the name is just the key or tag value (e.g. "park")
61196           function nameMatchesRawTag(lowercaseName, tags) {
61197             for (var i = 0; i < keysToTestForGenericValues.length; i++) {
61198               var key = keysToTestForGenericValues[i];
61199               var val = tags[key];
61200               if (val) {
61201                 val = val.toLowerCase();
61202                 if (key === lowercaseName ||
61203                   val === lowercaseName ||
61204                   key.replace(/\_/g, ' ') === lowercaseName ||
61205                   val.replace(/\_/g, ' ') === lowercaseName) {
61206                   return true;
61207                 }
61208               }
61209             }
61210             return false;
61211           }
61212
61213           function isGenericName(name, tags) {
61214             name = name.toLowerCase();
61215             return nameMatchesRawTag(name, tags) || isDiscardedSuggestionName(name);
61216           }
61217
61218           function makeGenericNameIssue(entityId, nameKey, genericName, langCode) {
61219             return new validationIssue({
61220               type: type,
61221               subtype: 'generic_name',
61222               severity: 'warning',
61223               message: function(context) {
61224                 var entity = context.hasEntity(this.entityIds[0]);
61225                 if (!entity) { return ''; }
61226                 var preset = _mainPresetIndex.match(entity, context.graph());
61227                 var langName = langCode && _mainLocalizer.languageName(langCode);
61228                 return _t('issues.generic_name.message' + (langName ? '_language' : ''),
61229                   { feature: preset.name(), name: genericName, language: langName }
61230                 );
61231               },
61232               reference: showReference,
61233               entityIds: [entityId],
61234               hash: nameKey + '=' + genericName,
61235               dynamicFixes: function() {
61236                 return [
61237                   new validationIssueFix({
61238                     icon: 'iD-operation-delete',
61239                     title: _t('issues.fix.remove_the_name.title'),
61240                     onClick: function(context) {
61241                       var entityId = this.issue.entityIds[0];
61242                       var entity = context.entity(entityId);
61243                       var tags = Object.assign({}, entity.tags);   // shallow copy
61244                       delete tags[nameKey];
61245                       context.perform(
61246                         actionChangeTags(entityId, tags), _t('issues.fix.remove_generic_name.annotation')
61247                       );
61248                     }
61249                   })
61250                 ];
61251               }
61252             });
61253
61254             function showReference(selection) {
61255               selection.selectAll('.issue-reference')
61256                 .data([0])
61257                 .enter()
61258                 .append('div')
61259                 .attr('class', 'issue-reference')
61260                 .text(_t('issues.generic_name.reference'));
61261             }
61262           }
61263
61264           function makeIncorrectNameIssue(entityId, nameKey, incorrectName, langCode) {
61265             return new validationIssue({
61266               type: type,
61267               subtype: 'not_name',
61268               severity: 'warning',
61269               message: function(context) {
61270                 var entity = context.hasEntity(this.entityIds[0]);
61271                 if (!entity) { return ''; }
61272                 var preset = _mainPresetIndex.match(entity, context.graph());
61273                 var langName = langCode && _mainLocalizer.languageName(langCode);
61274                 return _t('issues.incorrect_name.message' + (langName ? '_language' : ''),
61275                   { feature: preset.name(), name: incorrectName, language: langName }
61276                 );
61277               },
61278               reference: showReference,
61279               entityIds: [entityId],
61280               hash: nameKey + '=' + incorrectName,
61281               dynamicFixes: function() {
61282                 return [
61283                   new validationIssueFix({
61284                     icon: 'iD-operation-delete',
61285                     title: _t('issues.fix.remove_the_name.title'),
61286                     onClick: function(context) {
61287                       var entityId = this.issue.entityIds[0];
61288                       var entity = context.entity(entityId);
61289                       var tags = Object.assign({}, entity.tags);   // shallow copy
61290                       delete tags[nameKey];
61291                       context.perform(
61292                         actionChangeTags(entityId, tags), _t('issues.fix.remove_mistaken_name.annotation')
61293                       );
61294                     }
61295                   })
61296                 ];
61297               }
61298             });
61299
61300             function showReference(selection) {
61301               selection.selectAll('.issue-reference')
61302                 .data([0])
61303                 .enter()
61304                 .append('div')
61305                 .attr('class', 'issue-reference')
61306                 .text(_t('issues.generic_name.reference'));
61307             }
61308           }
61309
61310
61311           var validation = function checkGenericName(entity) {
61312             // a generic name is okay if it's a known brand or entity
61313             if (entity.hasWikidata()) { return []; }
61314
61315             var issues = [];
61316             var notNames = (entity.tags['not:name'] || '').split(';');
61317
61318             for (var key in entity.tags) {
61319               var m = key.match(/^name(?:(?::)([a-zA-Z_-]+))?$/);
61320               if (!m) { continue; }
61321
61322               var langCode = m.length >= 2 ? m[1] : null;
61323               var value = entity.tags[key];
61324               if (notNames.length) {
61325                 for (var i in notNames) {
61326                   var notName = notNames[i];
61327                   if (notName && value === notName) {
61328                     issues.push(makeIncorrectNameIssue(entity.id, key, value, langCode));
61329                     continue;
61330                   }
61331                 }
61332               }
61333               if (isGenericName(value, entity.tags)) {
61334                 issues.push(makeGenericNameIssue(entity.id, key, value, langCode));
61335               }
61336             }
61337
61338             return issues;
61339           };
61340
61341
61342           validation.type = type;
61343
61344           return validation;
61345         }
61346
61347         function validationUnsquareWay(context) {
61348             var type = 'unsquare_way';
61349             var DEFAULT_DEG_THRESHOLD = 5;   // see also issues.js
61350
61351             // use looser epsilon for detection to reduce warnings of buildings that are essentially square already
61352             var epsilon = 0.05;
61353             var nodeThreshold = 10;
61354
61355             function isBuilding(entity, graph) {
61356                 if (entity.type !== 'way' || entity.geometry(graph) !== 'area') { return false; }
61357                 return entity.tags.building && entity.tags.building !== 'no';
61358             }
61359
61360
61361             var validation = function checkUnsquareWay(entity, graph) {
61362
61363                 if (!isBuilding(entity, graph)) { return []; }
61364
61365                 // don't flag ways marked as physically unsquare
61366                 if (entity.tags.nonsquare === 'yes') { return []; }
61367
61368                 var isClosed = entity.isClosed();
61369                 if (!isClosed) { return []; }        // this building has bigger problems
61370
61371                 // don't flag ways with lots of nodes since they are likely detail-mapped
61372                 var nodes = graph.childNodes(entity).slice();    // shallow copy
61373                 if (nodes.length > nodeThreshold + 1) { return []; }   // +1 because closing node appears twice
61374
61375                 // ignore if not all nodes are fully downloaded
61376                 var osm = services.osm;
61377                 if (!osm || nodes.some(function(node) { return !osm.isDataLoaded(node.loc); })) { return []; }
61378
61379                 // don't flag connected ways to avoid unresolvable unsquare loops
61380                 var hasConnectedSquarableWays = nodes.some(function(node) {
61381                     return graph.parentWays(node).some(function(way) {
61382                         if (way.id === entity.id) { return false; }
61383                         if (isBuilding(way, graph)) { return true; }
61384                         return graph.parentRelations(way).some(function(parentRelation) {
61385                             return parentRelation.isMultipolygon() &&
61386                                 parentRelation.tags.building &&
61387                                 parentRelation.tags.building !== 'no';
61388                         });
61389                     });
61390                 });
61391                 if (hasConnectedSquarableWays) { return []; }
61392
61393
61394                 // user-configurable square threshold
61395                 var storedDegreeThreshold = corePreferences('validate-square-degrees');
61396                 var degreeThreshold = isNaN(storedDegreeThreshold) ? DEFAULT_DEG_THRESHOLD : parseFloat(storedDegreeThreshold);
61397
61398                 var points = nodes.map(function(node) { return context.projection(node.loc); });
61399                 if (!geoOrthoCanOrthogonalize(points, isClosed, epsilon, degreeThreshold, true)) { return []; }
61400
61401                 var autoArgs;
61402                 // don't allow autosquaring features linked to wikidata
61403                 if (!entity.tags.wikidata) {
61404                     // use same degree threshold as for detection
61405                     var autoAction = actionOrthogonalize(entity.id, context.projection, undefined, degreeThreshold);
61406                     autoAction.transitionable = false;  // when autofixing, do it instantly
61407                     autoArgs = [autoAction, _t('operations.orthogonalize.annotation.feature.single')];
61408                 }
61409
61410                 return [new validationIssue({
61411                     type: type,
61412                     subtype: 'building',
61413                     severity: 'warning',
61414                     message: function(context) {
61415                         var entity = context.hasEntity(this.entityIds[0]);
61416                         return entity ? _t('issues.unsquare_way.message', { feature: utilDisplayLabel(entity, context.graph()) }) : '';
61417                     },
61418                     reference: showReference,
61419                     entityIds: [entity.id],
61420                     hash: JSON.stringify(autoArgs !== undefined) + degreeThreshold,
61421                     dynamicFixes: function() {
61422                         return [
61423                             new validationIssueFix({
61424                                 icon: 'iD-operation-orthogonalize',
61425                                 title: _t('issues.fix.square_feature.title'),
61426                                 autoArgs: autoArgs,
61427                                 onClick: function(context, completionHandler) {
61428                                     var entityId = this.issue.entityIds[0];
61429                                     // use same degree threshold as for detection
61430                                     context.perform(
61431                                         actionOrthogonalize(entityId, context.projection, undefined, degreeThreshold),
61432                                         _t('operations.orthogonalize.annotation.feature.single')
61433                                     );
61434                                     // run after the squaring transition (currently 150ms)
61435                                     window.setTimeout(function() { completionHandler(); }, 175);
61436                                 }
61437                             }) ];
61438                     }
61439                 })];
61440
61441                 function showReference(selection) {
61442                     selection.selectAll('.issue-reference')
61443                         .data([0])
61444                         .enter()
61445                         .append('div')
61446                         .attr('class', 'issue-reference')
61447                         .text(_t('issues.unsquare_way.buildings.reference'));
61448                 }
61449             };
61450
61451             validation.type = type;
61452
61453             return validation;
61454         }
61455
61456         var Validations = /*#__PURE__*/Object.freeze({
61457                 __proto__: null,
61458                 validationAlmostJunction: validationAlmostJunction,
61459                 validationCloseNodes: validationCloseNodes,
61460                 validationCrossingWays: validationCrossingWays,
61461                 validationDisconnectedWay: validationDisconnectedWay,
61462                 validationFormatting: validationFormatting,
61463                 validationHelpRequest: validationHelpRequest,
61464                 validationImpossibleOneway: validationImpossibleOneway,
61465                 validationIncompatibleSource: validationIncompatibleSource,
61466                 validationMaprules: validationMaprules,
61467                 validationMismatchedGeometry: validationMismatchedGeometry,
61468                 validationMissingRole: validationMissingRole,
61469                 validationMissingTag: validationMissingTag,
61470                 validationOutdatedTags: validationOutdatedTags,
61471                 validationPrivateData: validationPrivateData,
61472                 validationSuspiciousName: validationSuspiciousName,
61473                 validationUnsquareWay: validationUnsquareWay
61474         });
61475
61476         function coreValidator(context) {
61477             var dispatch$1 = dispatch('validated', 'focusedIssue');
61478             var validator = utilRebind({}, dispatch$1, 'on');
61479
61480             var _rules = {};
61481             var _disabledRules = {};
61482
61483             var _ignoredIssueIDs = {};          // issue.id -> true
61484             var _baseCache = validationCache(); // issues before any user edits
61485             var _headCache = validationCache(); // issues after all user edits
61486             var _validatedGraph = null;
61487             var _deferred = new Set();
61488
61489             //
61490             // initialize the validator rulesets
61491             //
61492             validator.init = function() {
61493                 Object.values(Validations).forEach(function(validation) {
61494                     if (typeof validation !== 'function') { return; }
61495
61496                     var fn = validation(context);
61497                     var key = fn.type;
61498                     _rules[key] = fn;
61499                 });
61500
61501                 var disabledRules = corePreferences('validate-disabledRules');
61502                 if (disabledRules) {
61503                     disabledRules.split(',')
61504                         .forEach(function(key) { _disabledRules[key] = true; });
61505                 }
61506             };
61507
61508
61509             //
61510             // clear caches, called whenever iD resets after a save
61511             //
61512             validator.reset = function() {
61513                 Array.from(_deferred).forEach(function(handle) {
61514                     window.cancelIdleCallback(handle);
61515                     _deferred.delete(handle);
61516                 });
61517
61518                 // clear caches
61519                 _ignoredIssueIDs = {};
61520                 _baseCache = validationCache();
61521                 _headCache = validationCache();
61522                 _validatedGraph = null;
61523             };
61524
61525             validator.resetIgnoredIssues = function() {
61526                 _ignoredIssueIDs = {};
61527                 // reload UI
61528                 dispatch$1.call('validated');
61529             };
61530
61531
61532             // must update issues when the user changes the unsquare thereshold
61533             validator.reloadUnsquareIssues = function() {
61534
61535                 reloadUnsquareIssues(_headCache, context.graph());
61536                 reloadUnsquareIssues(_baseCache, context.history().base());
61537
61538                 dispatch$1.call('validated');
61539             };
61540
61541             function reloadUnsquareIssues(cache, graph) {
61542
61543                 var checkUnsquareWay = _rules.unsquare_way;
61544                 if (typeof checkUnsquareWay !== 'function') { return; }
61545
61546                 // uncache existing
61547                 cache.uncacheIssuesOfType('unsquare_way');
61548
61549                 var buildings = context.history().tree().intersects(geoExtent([-180,-90],[180, 90]), graph)  // everywhere
61550                     .filter(function(entity) {
61551                         return entity.type === 'way' && entity.tags.building && entity.tags.building !== 'no';
61552                     });
61553
61554                 // rerun for all buildings
61555                 buildings.forEach(function(entity) {
61556                     var detected = checkUnsquareWay(entity, graph);
61557                     if (detected.length !== 1) { return; }
61558                     var issue = detected[0];
61559                     if (!cache.issuesByEntityID[entity.id]) {
61560                         cache.issuesByEntityID[entity.id] = new Set();
61561                     }
61562                     cache.issuesByEntityID[entity.id].add(issue.id);
61563                     cache.issuesByIssueID[issue.id] = issue;
61564                 });
61565             }
61566
61567             // options = {
61568             //     what: 'all',     // 'all' or 'edited'
61569             //     where: 'all',   // 'all' or 'visible'
61570             //     includeIgnored: false   // true, false, or 'only'
61571             //     includeDisabledRules: false   // true, false, or 'only'
61572             // };
61573             validator.getIssues = function(options) {
61574                 var opts = Object.assign({ what: 'all', where: 'all', includeIgnored: false, includeDisabledRules: false }, options);
61575                 var issues = Object.values(_headCache.issuesByIssueID);
61576                 var view = context.map().extent();
61577
61578                 return issues.filter(function(issue) {
61579                     if (!issue) { return false; }
61580                     if (opts.includeDisabledRules === 'only' && !_disabledRules[issue.type]) { return false; }
61581                     if (!opts.includeDisabledRules && _disabledRules[issue.type]) { return false; }
61582
61583                     if (opts.includeIgnored === 'only' && !_ignoredIssueIDs[issue.id]) { return false; }
61584                     if (!opts.includeIgnored && _ignoredIssueIDs[issue.id]) { return false; }
61585
61586                     // Sanity check:  This issue may be for an entity that not longer exists.
61587                     // If we detect this, uncache and return false so it is not included..
61588                     var entityIds = issue.entityIds || [];
61589                     for (var i = 0; i < entityIds.length; i++) {
61590                         var entityId = entityIds[i];
61591                         if (!context.hasEntity(entityId)) {
61592                             delete _headCache.issuesByEntityID[entityId];
61593                             delete _headCache.issuesByIssueID[issue.id];
61594                             return false;
61595                         }
61596                     }
61597
61598                     if (opts.what === 'edited' && _baseCache.issuesByIssueID[issue.id]) { return false; }
61599
61600                     if (opts.where === 'visible') {
61601                         var extent = issue.extent(context.graph());
61602                         if (!view.intersects(extent)) { return false; }
61603                     }
61604
61605                     return true;
61606                 });
61607             };
61608
61609             validator.getResolvedIssues = function() {
61610                 var baseIssues = Object.values(_baseCache.issuesByIssueID);
61611                 return baseIssues.filter(function(issue) {
61612                     return !_headCache.issuesByIssueID[issue.id];
61613                 });
61614             };
61615
61616             validator.focusIssue = function(issue) {
61617                 var extent = issue.extent(context.graph());
61618
61619                 if (extent) {
61620                     var setZoom = Math.max(context.map().zoom(), 19);
61621                     context.map().unobscuredCenterZoomEase(extent.center(), setZoom);
61622
61623                     // select the first entity
61624                     if (issue.entityIds && issue.entityIds.length) {
61625                         window.setTimeout(function() {
61626                             var ids = issue.entityIds;
61627                             context.enter(modeSelect(context, [ids[0]]));
61628                             dispatch$1.call('focusedIssue', this, issue);
61629                         }, 250);  // after ease
61630                     }
61631                 }
61632             };
61633
61634
61635             validator.getIssuesBySeverity = function(options) {
61636                 var groups = utilArrayGroupBy(validator.getIssues(options), 'severity');
61637                 groups.error = groups.error || [];
61638                 groups.warning = groups.warning || [];
61639                 return groups;
61640             };
61641
61642             // show some issue types in a particular order
61643             var orderedIssueTypes = [
61644                 // flag missing data first
61645                 'missing_tag', 'missing_role',
61646                 // then flag identity issues
61647                 'outdated_tags', 'mismatched_geometry',
61648                 // flag geometry issues where fixing them might solve connectivity issues
61649                 'crossing_ways', 'almost_junction',
61650                 // then flag connectivity issues
61651                 'disconnected_way', 'impossible_oneway'
61652             ];
61653
61654             // returns the issues that the given entity IDs have in common, matching the given options
61655             validator.getSharedEntityIssues = function(entityIDs, options) {
61656                 var cache = _headCache;
61657
61658                 // gather the issues that are common to all the entities
61659                 var issueIDs = entityIDs.reduce(function(acc, entityID) {
61660                     var entityIssueIDs = cache.issuesByEntityID[entityID] || new Set();
61661                     if (!acc) {
61662                         return new Set(entityIssueIDs);
61663                     }
61664                     return new Set([].concat( acc ).filter(function(elem) {
61665                         return entityIssueIDs.has(elem);
61666                     }));
61667                 }, null) || [];
61668
61669                 var opts = options || {};
61670
61671                 return Array.from(issueIDs)
61672                     .map(function(id) { return cache.issuesByIssueID[id]; })
61673                     .filter(function(issue) {
61674                         if (!issue) { return false; }
61675                         if (opts.includeDisabledRules === 'only' && !_disabledRules[issue.type]) { return false; }
61676                         if (!opts.includeDisabledRules && _disabledRules[issue.type]) { return false; }
61677
61678                         if (opts.includeIgnored === 'only' && !_ignoredIssueIDs[issue.id]) { return false; }
61679                         if (!opts.includeIgnored && _ignoredIssueIDs[issue.id]) { return false; }
61680
61681                         return true;
61682                     }).sort(function(issue1, issue2) {
61683                         if (issue1.type === issue2.type) {
61684                             // issues of the same type, sort deterministically
61685                             return issue1.id < issue2.id ? -1 : 1;
61686                         }
61687                         var index1 = orderedIssueTypes.indexOf(issue1.type);
61688                         var index2 = orderedIssueTypes.indexOf(issue2.type);
61689                         if (index1 !== -1 && index2 !== -1) {
61690                             // both issue types have explicit sort orders
61691                             return index1 - index2;
61692                         } else if (index1 === -1 && index2 === -1) {
61693                             // neither issue type has an explicit sort order, sort by type
61694                             return issue1.type < issue2.type ? -1 : 1;
61695                         } else {
61696                             // order explicit types before everything else
61697                             return index1 !== -1 ? -1 : 1;
61698                         }
61699                     });
61700             };
61701
61702
61703             validator.getEntityIssues = function(entityID, options) {
61704                 return validator.getSharedEntityIssues([entityID], options);
61705             };
61706
61707
61708             validator.getRuleKeys = function() {
61709                 return Object.keys(_rules);
61710             };
61711
61712
61713             validator.isRuleEnabled = function(key) {
61714                 return !_disabledRules[key];
61715             };
61716
61717
61718             validator.toggleRule = function(key) {
61719                 if (_disabledRules[key]) {
61720                     delete _disabledRules[key];
61721                 } else {
61722                     _disabledRules[key] = true;
61723                 }
61724
61725                 corePreferences('validate-disabledRules', Object.keys(_disabledRules).join(','));
61726                 validator.validate();
61727             };
61728
61729
61730             validator.disableRules = function(keys) {
61731                 _disabledRules = {};
61732                 keys.forEach(function(k) {
61733                     _disabledRules[k] = true;
61734                 });
61735
61736                 corePreferences('validate-disabledRules', Object.keys(_disabledRules).join(','));
61737                 validator.validate();
61738             };
61739
61740
61741             validator.ignoreIssue = function(id) {
61742                 _ignoredIssueIDs[id] = true;
61743             };
61744
61745
61746             //
61747             // Run validation on a single entity for the given graph
61748             //
61749             function validateEntity(entity, graph) {
61750                 var entityIssues = [];
61751
61752                 // runs validation and appends resulting issues
61753                 function runValidation(key) {
61754
61755                     var fn = _rules[key];
61756                     if (typeof fn !== 'function') {
61757                         console.error('no such validation rule = ' + key);  // eslint-disable-line no-console
61758                         return;
61759                     }
61760
61761                     var detected = fn(entity, graph);
61762                     entityIssues = entityIssues.concat(detected);
61763                 }
61764
61765                 // run all rules
61766                 Object.keys(_rules).forEach(runValidation);
61767
61768                 return entityIssues;
61769             }
61770
61771             function entityIDsToValidate(entityIDs, graph) {
61772                 var processedIDs = new Set();
61773                 return entityIDs.reduce(function(acc, entityID) {
61774                     // keep redundancy check separate from `acc` because an `entityID`
61775                     // could have been added to `acc` as a related entity through an earlier pass
61776                     if (processedIDs.has(entityID)) { return acc; }
61777                     processedIDs.add(entityID);
61778
61779                     var entity = graph.hasEntity(entityID);
61780                     if (!entity) { return acc; }
61781
61782                     acc.add(entityID);
61783
61784                     var checkParentRels = [entity];
61785
61786                     if (entity.type === 'node') {
61787                         graph.parentWays(entity).forEach(function(parentWay) {
61788                             acc.add(parentWay.id); // include parent ways
61789                             checkParentRels.push(parentWay);
61790                         });
61791                     } else if (entity.type === 'relation') {
61792                         entity.members.forEach(function(member) {
61793                             acc.add(member.id); // include members
61794                         });
61795                     } else if (entity.type === 'way') {
61796                         entity.nodes.forEach(function(nodeID) {
61797                             acc.add(nodeID); // include child nodes
61798                             graph._parentWays[nodeID].forEach(function(wayID) {
61799                                 acc.add(wayID); // include connected ways
61800                             });
61801                         });
61802                     }
61803
61804                     checkParentRels.forEach(function(entity) {   // include parent relations
61805                         if (entity.type !== 'relation') {        // but not super-relations
61806                             graph.parentRelations(entity).forEach(function(parentRelation) {
61807                                 acc.add(parentRelation.id);
61808                             });
61809                         }
61810                     });
61811
61812                     return acc;
61813
61814                 }, new Set());
61815             }
61816
61817             //
61818             // Run validation for several entities, supplied `entityIDs`,
61819             // against `graph` for the given `cache`
61820             //
61821             function validateEntities(entityIDs, graph, cache) {
61822
61823                 // clear caches for existing issues related to these entities
61824                 entityIDs.forEach(cache.uncacheEntityID);
61825
61826                 // detect new issues and update caches
61827                 entityIDs.forEach(function(entityID) {
61828                     var entity = graph.hasEntity(entityID);
61829                     // don't validate deleted entities
61830                     if (!entity) { return; }
61831
61832                     var issues = validateEntity(entity, graph);
61833                     cache.cacheIssues(issues);
61834                 });
61835             }
61836
61837
61838             //
61839             // Validates anything that has changed since the last time it was run.
61840             // Also updates the "validatedGraph" to be the current graph
61841             // and dispatches a `validated` event when finished.
61842             //
61843             validator.validate = function() {
61844
61845                 var currGraph = context.graph();
61846                 _validatedGraph = _validatedGraph || context.history().base();
61847                 if (currGraph === _validatedGraph) {
61848                     dispatch$1.call('validated');
61849                     return;
61850                 }
61851                 var oldGraph = _validatedGraph;
61852                 var difference = coreDifference(oldGraph, currGraph);
61853                 _validatedGraph = currGraph;
61854
61855                 var createdAndModifiedEntityIDs = difference.extantIDs(true);   // created/modified (true = w/relation members)
61856                 var entityIDsToCheck = entityIDsToValidate(createdAndModifiedEntityIDs, currGraph);
61857
61858                 // check modified and deleted entities against the old graph in order to update their related entities
61859                 // (e.g. deleting the only highway connected to a road should create a disconnected highway issue)
61860                 var modifiedAndDeletedEntityIDs = difference.deleted().concat(difference.modified())
61861                     .map(function(entity) { return entity.id; });
61862                 var entityIDsToCheckForOldGraph = entityIDsToValidate(modifiedAndDeletedEntityIDs, oldGraph);
61863
61864                 // concat the sets
61865                 entityIDsToCheckForOldGraph.forEach(entityIDsToCheck.add, entityIDsToCheck);
61866
61867                 validateEntities(entityIDsToCheck, context.graph(), _headCache);
61868
61869                 dispatch$1.call('validated');
61870             };
61871
61872
61873             // WHEN TO RUN VALIDATION:
61874             // When graph changes:
61875             context.history()
61876                 .on('restore.validator', validator.validate)   // restore saved history
61877                 .on('undone.validator', validator.validate)    // undo
61878                 .on('redone.validator', validator.validate);   // redo
61879                 // but not on 'change' (e.g. while drawing)
61880
61881             // When user changes editing modes:
61882             context
61883                 .on('exit.validator', validator.validate);
61884
61885             // When merging fetched data:
61886             context.history()
61887                 .on('merge.validator', function(entities) {
61888                     if (!entities) { return; }
61889                     var handle = window.requestIdleCallback(function() {
61890                         var entityIDs = entities.map(function(entity) { return entity.id; });
61891                         var headGraph = context.graph();
61892                         validateEntities(entityIDsToValidate(entityIDs, headGraph), headGraph, _headCache);
61893
61894                         var baseGraph = context.history().base();
61895                         validateEntities(entityIDsToValidate(entityIDs, baseGraph), baseGraph, _baseCache);
61896
61897                         dispatch$1.call('validated');
61898                     });
61899                     _deferred.add(handle);
61900                 });
61901
61902
61903             return validator;
61904         }
61905
61906
61907         function validationCache() {
61908
61909             var cache = {
61910                 issuesByIssueID: {},  // issue.id -> issue
61911                 issuesByEntityID: {} // entity.id -> set(issue.id)
61912             };
61913
61914             cache.cacheIssues = function(issues) {
61915                 issues.forEach(function(issue) {
61916                     var entityIds = issue.entityIds || [];
61917                     entityIds.forEach(function(entityId) {
61918                         if (!cache.issuesByEntityID[entityId]) {
61919                             cache.issuesByEntityID[entityId] = new Set();
61920                         }
61921                         cache.issuesByEntityID[entityId].add(issue.id);
61922                     });
61923                     cache.issuesByIssueID[issue.id] = issue;
61924                 });
61925             };
61926
61927             cache.uncacheIssue = function(issue) {
61928                 // When multiple entities are involved (e.g. crossing_ways),
61929                 // remove this issue from the other entity caches too..
61930                 var entityIds = issue.entityIds || [];
61931                 entityIds.forEach(function(entityId) {
61932                     if (cache.issuesByEntityID[entityId]) {
61933                         cache.issuesByEntityID[entityId].delete(issue.id);
61934                     }
61935                 });
61936                 delete cache.issuesByIssueID[issue.id];
61937             };
61938
61939             cache.uncacheIssues = function(issues) {
61940                 issues.forEach(cache.uncacheIssue);
61941             };
61942
61943             cache.uncacheIssuesOfType = function(type) {
61944                 var issuesOfType = Object.values(cache.issuesByIssueID)
61945                     .filter(function(issue) { return issue.type === type; });
61946                 cache.uncacheIssues(issuesOfType);
61947             };
61948
61949             //
61950             // Remove a single entity and all its related issues from the caches
61951             //
61952             cache.uncacheEntityID = function(entityID) {
61953                 var issueIDs = cache.issuesByEntityID[entityID];
61954                 if (!issueIDs) { return; }
61955
61956                 issueIDs.forEach(function(issueID) {
61957                     var issue = cache.issuesByIssueID[issueID];
61958                     if (issue) {
61959                         cache.uncacheIssue(issue);
61960                     } else {
61961                         delete cache.issuesByIssueID[issueID];
61962                     }
61963                 });
61964
61965                 delete cache.issuesByEntityID[entityID];
61966             };
61967
61968             return cache;
61969         }
61970
61971         function coreUploader(context) {
61972
61973             var dispatch$1 = dispatch(
61974                 // Start and end events are dispatched exactly once each per legitimate outside call to `save`
61975                 'saveStarted', // dispatched as soon as a call to `save` has been deemed legitimate
61976                 'saveEnded',   // dispatched after the result event has been dispatched
61977
61978                 'willAttemptUpload', // dispatched before the actual upload call occurs, if it will
61979                 'progressChanged',
61980
61981                 // Each save results in one of these outcomes:
61982                 'resultNoChanges', // upload wasn't attempted since there were no edits
61983                 'resultErrors',    // upload failed due to errors
61984                 'resultConflicts', // upload failed due to data conflicts
61985                 'resultSuccess'    // upload completed without errors
61986             );
61987
61988             var _isSaving = false;
61989
61990             var _conflicts = [];
61991             var _errors = [];
61992             var _origChanges;
61993
61994             var _discardTags = {};
61995             _mainFileFetcher.get('discarded')
61996                 .then(function(d) { _discardTags = d; })
61997                 .catch(function() { /* ignore */ });
61998
61999             var uploader = utilRebind({}, dispatch$1, 'on');
62000
62001             uploader.isSaving = function() {
62002                 return _isSaving;
62003             };
62004
62005             uploader.save = function(changeset, tryAgain, checkConflicts) {
62006                 // Guard against accidentally entering save code twice - #4641
62007                 if (_isSaving && !tryAgain) {
62008                     return;
62009                 }
62010
62011                 var osm = context.connection();
62012                 if (!osm) { return; }
62013
62014                 // If user somehow got logged out mid-save, try to reauthenticate..
62015                 // This can happen if they were logged in from before, but the tokens are no longer valid.
62016                 if (!osm.authenticated()) {
62017                     osm.authenticate(function(err) {
62018                         if (!err) {
62019                             uploader.save(changeset, tryAgain, checkConflicts);  // continue where we left off..
62020                         }
62021                     });
62022                     return;
62023                 }
62024
62025                 if (!_isSaving) {
62026                     _isSaving = true;
62027                     dispatch$1.call('saveStarted', this);
62028                 }
62029
62030                 var history = context.history();
62031
62032                 _conflicts = [];
62033                 _errors = [];
62034
62035                 // Store original changes, in case user wants to download them as an .osc file
62036                 _origChanges = history.changes(actionDiscardTags(history.difference(), _discardTags));
62037
62038                 // First time, `history.perform` a no-op action.
62039                 // Any conflict resolutions will be done as `history.replace`
62040                 // Remember to pop this later if needed
62041                 if (!tryAgain) {
62042                     history.perform(actionNoop());
62043                 }
62044
62045                 // Attempt a fast upload.. If there are conflicts, re-enter with `checkConflicts = true`
62046                 if (!checkConflicts) {
62047                     upload(changeset);
62048
62049                 // Do the full (slow) conflict check..
62050                 } else {
62051                     performFullConflictCheck(changeset);
62052                 }
62053
62054             };
62055
62056
62057             function performFullConflictCheck(changeset) {
62058
62059                 var osm = context.connection();
62060                 if (!osm) { return; }
62061
62062                 var history = context.history();
62063
62064                 var localGraph = context.graph();
62065                 var remoteGraph = coreGraph(history.base(), true);
62066
62067                 var summary = history.difference().summary();
62068                 var _toCheck = [];
62069                 for (var i = 0; i < summary.length; i++) {
62070                     var item = summary[i];
62071                     if (item.changeType === 'modified') {
62072                         _toCheck.push(item.entity.id);
62073                     }
62074                 }
62075
62076                 var _toLoad = withChildNodes(_toCheck, localGraph);
62077                 var _loaded = {};
62078                 var _toLoadCount = 0;
62079                 var _toLoadTotal = _toLoad.length;
62080
62081                 if (_toCheck.length) {
62082                     dispatch$1.call('progressChanged', this, _toLoadCount, _toLoadTotal);
62083                     _toLoad.forEach(function(id) { _loaded[id] = false; });
62084                     osm.loadMultiple(_toLoad, loaded);
62085                 } else {
62086                     upload(changeset);
62087                 }
62088
62089                 return;
62090
62091                 function withChildNodes(ids, graph) {
62092                     var s = new Set(ids);
62093                     ids.forEach(function(id) {
62094                         var entity = graph.entity(id);
62095                         if (entity.type !== 'way') { return; }
62096
62097                         graph.childNodes(entity).forEach(function(child) {
62098                             if (child.version !== undefined) {
62099                                 s.add(child.id);
62100                             }
62101                         });
62102                     });
62103
62104                     return Array.from(s);
62105                 }
62106
62107
62108                 // Reload modified entities into an alternate graph and check for conflicts..
62109                 function loaded(err, result) {
62110                     if (_errors.length) { return; }
62111
62112                     if (err) {
62113                         _errors.push({
62114                             msg: err.message || err.responseText,
62115                             details: [ _t('save.status_code', { code: err.status }) ]
62116                         });
62117                         didResultInErrors();
62118
62119                     } else {
62120                         var loadMore = [];
62121
62122                         result.data.forEach(function(entity) {
62123                             remoteGraph.replace(entity);
62124                             _loaded[entity.id] = true;
62125                             _toLoad = _toLoad.filter(function(val) { return val !== entity.id; });
62126
62127                             if (!entity.visible) { return; }
62128
62129                             // Because loadMultiple doesn't download /full like loadEntity,
62130                             // need to also load children that aren't already being checked..
62131                             var i, id;
62132                             if (entity.type === 'way') {
62133                                 for (i = 0; i < entity.nodes.length; i++) {
62134                                     id = entity.nodes[i];
62135                                     if (_loaded[id] === undefined) {
62136                                         _loaded[id] = false;
62137                                         loadMore.push(id);
62138                                     }
62139                                 }
62140                             } else if (entity.type === 'relation' && entity.isMultipolygon()) {
62141                                 for (i = 0; i < entity.members.length; i++) {
62142                                     id = entity.members[i].id;
62143                                     if (_loaded[id] === undefined) {
62144                                         _loaded[id] = false;
62145                                         loadMore.push(id);
62146                                     }
62147                                 }
62148                             }
62149                         });
62150
62151                         _toLoadCount += result.data.length;
62152                         _toLoadTotal += loadMore.length;
62153                         dispatch$1.call('progressChanged', this, _toLoadCount, _toLoadTotal);
62154
62155                         if (loadMore.length) {
62156                             _toLoad.push.apply(_toLoad, loadMore);
62157                             osm.loadMultiple(loadMore, loaded);
62158                         }
62159
62160                         if (!_toLoad.length) {
62161                             detectConflicts();
62162                             upload(changeset);
62163                         }
62164                     }
62165                 }
62166
62167
62168                 function detectConflicts() {
62169                     function choice(id, text, action) {
62170                         return {
62171                             id: id,
62172                             text: text,
62173                             action: function() {
62174                                 history.replace(action);
62175                             }
62176                         };
62177                     }
62178                     function formatUser(d) {
62179                         return '<a href="' + osm.userURL(d) + '" target="_blank">' + d + '</a>';
62180                     }
62181                     function entityName(entity) {
62182                         return utilDisplayName(entity) || (utilDisplayType(entity.id) + ' ' + entity.id);
62183                     }
62184
62185                     function sameVersions(local, remote) {
62186                         if (local.version !== remote.version) { return false; }
62187
62188                         if (local.type === 'way') {
62189                             var children = utilArrayUnion(local.nodes, remote.nodes);
62190                             for (var i = 0; i < children.length; i++) {
62191                                 var a = localGraph.hasEntity(children[i]);
62192                                 var b = remoteGraph.hasEntity(children[i]);
62193                                 if (a && b && a.version !== b.version) { return false; }
62194                             }
62195                         }
62196
62197                         return true;
62198                     }
62199
62200                     _toCheck.forEach(function(id) {
62201                         var local = localGraph.entity(id);
62202                         var remote = remoteGraph.entity(id);
62203
62204                         if (sameVersions(local, remote)) { return; }
62205
62206                         var merge = actionMergeRemoteChanges(id, localGraph, remoteGraph, _discardTags, formatUser);
62207
62208                         history.replace(merge);
62209
62210                         var mergeConflicts = merge.conflicts();
62211                         if (!mergeConflicts.length) { return; }  // merged safely
62212
62213                         var forceLocal = actionMergeRemoteChanges(id, localGraph, remoteGraph, _discardTags).withOption('force_local');
62214                         var forceRemote = actionMergeRemoteChanges(id, localGraph, remoteGraph, _discardTags).withOption('force_remote');
62215                         var keepMine = _t('save.conflict.' + (remote.visible ? 'keep_local' : 'restore'));
62216                         var keepTheirs = _t('save.conflict.' + (remote.visible ? 'keep_remote' : 'delete'));
62217
62218                         _conflicts.push({
62219                             id: id,
62220                             name: entityName(local),
62221                             details: mergeConflicts,
62222                             chosen: 1,
62223                             choices: [
62224                                 choice(id, keepMine, forceLocal),
62225                                 choice(id, keepTheirs, forceRemote)
62226                             ]
62227                         });
62228                     });
62229                 }
62230             }
62231
62232
62233             function upload(changeset) {
62234                 var osm = context.connection();
62235                 if (!osm) {
62236                     _errors.push({ msg: 'No OSM Service' });
62237                 }
62238
62239                 if (_conflicts.length) {
62240                     didResultInConflicts(changeset);
62241
62242                 } else if (_errors.length) {
62243                     didResultInErrors();
62244
62245                 } else {
62246                     var history = context.history();
62247                     var changes = history.changes(actionDiscardTags(history.difference(), _discardTags));
62248                     if (changes.modified.length || changes.created.length || changes.deleted.length) {
62249
62250                         dispatch$1.call('willAttemptUpload', this);
62251
62252                         osm.putChangeset(changeset, changes, uploadCallback);
62253
62254                     } else {
62255                         // changes were insignificant or reverted by user
62256                         didResultInNoChanges();
62257                     }
62258                 }
62259             }
62260
62261
62262             function uploadCallback(err, changeset) {
62263                 if (err) {
62264                     if (err.status === 409) {  // 409 Conflict
62265                         uploader.save(changeset, true, true);  // tryAgain = true, checkConflicts = true
62266                     } else {
62267                         _errors.push({
62268                             msg: err.message || err.responseText,
62269                             details: [ _t('save.status_code', { code: err.status }) ]
62270                         });
62271                         didResultInErrors();
62272                     }
62273
62274                 } else {
62275                     didResultInSuccess(changeset);
62276                 }
62277             }
62278
62279             function didResultInNoChanges() {
62280
62281                 dispatch$1.call('resultNoChanges', this);
62282
62283                 endSave();
62284
62285                 context.flush(); // reset iD
62286             }
62287
62288             function didResultInErrors() {
62289
62290                 context.history().pop();
62291
62292                 dispatch$1.call('resultErrors', this, _errors);
62293
62294                 endSave();
62295             }
62296
62297
62298             function didResultInConflicts(changeset) {
62299
62300                 _conflicts.sort(function(a, b) { return b.id.localeCompare(a.id); });
62301
62302                 dispatch$1.call('resultConflicts', this, changeset, _conflicts, _origChanges);
62303
62304                 endSave();
62305             }
62306
62307
62308             function didResultInSuccess(changeset) {
62309
62310                 // delete the edit stack cached to local storage
62311                 context.history().clearSaved();
62312
62313                 dispatch$1.call('resultSuccess', this, changeset);
62314
62315                 // Add delay to allow for postgres replication #1646 #2678
62316                 window.setTimeout(function() {
62317
62318                     endSave();
62319
62320                     context.flush(); // reset iD
62321                 }, 2500);
62322             }
62323
62324
62325             function endSave() {
62326                 _isSaving = false;
62327
62328                 dispatch$1.call('saveEnded', this);
62329             }
62330
62331
62332             uploader.cancelConflictResolution = function() {
62333                 context.history().pop();
62334             };
62335
62336
62337             uploader.processResolvedConflicts = function(changeset) {
62338                 var history = context.history();
62339
62340                 for (var i = 0; i < _conflicts.length; i++) {
62341                     if (_conflicts[i].chosen === 1) {  // user chose "use theirs"
62342                         var entity = context.hasEntity(_conflicts[i].id);
62343                         if (entity && entity.type === 'way') {
62344                             var children = utilArrayUniq(entity.nodes);
62345                             for (var j = 0; j < children.length; j++) {
62346                                 history.replace(actionRevert(children[j]));
62347                             }
62348                         }
62349                         history.replace(actionRevert(_conflicts[i].id));
62350                     }
62351                 }
62352
62353                 uploader.save(changeset, true, false);  // tryAgain = true, checkConflicts = false
62354             };
62355
62356
62357             uploader.reset = function() {
62358
62359             };
62360
62361
62362             return uploader;
62363         }
62364
62365         var isRetina = window.devicePixelRatio && window.devicePixelRatio >= 2;
62366
62367         // listen for DPI change, e.g. when dragging a browser window from a retina to non-retina screen
62368         window.matchMedia("\n        (-webkit-min-device-pixel-ratio: 2), /* Safari */\n        (min-resolution: 2dppx),             /* standard */\n        (min-resolution: 192dpi)             /* fallback */\n    ").addListener(function() {
62369
62370             isRetina = window.devicePixelRatio && window.devicePixelRatio >= 2;
62371         });
62372
62373
62374         function localeDateString(s) {
62375             if (!s) { return null; }
62376             var options = { day: 'numeric', month: 'short', year: 'numeric' };
62377             var d = new Date(s);
62378             if (isNaN(d.getTime())) { return null; }
62379             return d.toLocaleDateString(_mainLocalizer.localeCode(), options);
62380         }
62381
62382         function vintageRange(vintage) {
62383             var s;
62384             if (vintage.start || vintage.end) {
62385                 s = (vintage.start || '?');
62386                 if (vintage.start !== vintage.end) {
62387                     s += ' - ' + (vintage.end || '?');
62388                 }
62389             }
62390             return s;
62391         }
62392
62393
62394         function rendererBackgroundSource(data) {
62395             var source = Object.assign({}, data);   // shallow copy
62396             var _offset = [0, 0];
62397             var _name = source.name;
62398             var _description = source.description;
62399             var _best = !!source.best;
62400             var _template = source.encrypted ? utilAesDecrypt(source.template) : source.template;
62401
62402             source.tileSize = data.tileSize || 256;
62403             source.zoomExtent = data.zoomExtent || [0, 22];
62404             source.overzoom = data.overzoom !== false;
62405
62406             source.offset = function(val) {
62407                 if (!arguments.length) { return _offset; }
62408                 _offset = val;
62409                 return source;
62410             };
62411
62412
62413             source.nudge = function(val, zoomlevel) {
62414                 _offset[0] += val[0] / Math.pow(2, zoomlevel);
62415                 _offset[1] += val[1] / Math.pow(2, zoomlevel);
62416                 return source;
62417             };
62418
62419
62420             source.name = function() {
62421                 var id_safe = source.id.replace(/\./g, '<TX_DOT>');
62422                 return _t('imagery.' + id_safe + '.name', { default: _name });
62423             };
62424
62425
62426             source.description = function() {
62427                 var id_safe = source.id.replace(/\./g, '<TX_DOT>');
62428                 return _t('imagery.' + id_safe + '.description', { default: _description });
62429             };
62430
62431
62432             source.best = function() {
62433                 return _best;
62434             };
62435
62436
62437             source.area = function() {
62438                 if (!data.polygon) { return Number.MAX_VALUE; }  // worldwide
62439                 var area = d3_geoArea({ type: 'MultiPolygon', coordinates: [ data.polygon ] });
62440                 return isNaN(area) ? 0 : area;
62441             };
62442
62443
62444             source.imageryUsed = function() {
62445                 return name || source.id;
62446             };
62447
62448
62449             source.template = function(val) {
62450                 if (!arguments.length) { return _template; }
62451                 if (source.id === 'custom') {
62452                     _template = val;
62453                 }
62454                 return source;
62455             };
62456
62457
62458             source.url = function(coord) {
62459                 var result = _template;
62460                 if (result === '') { return result; }   // source 'none'
62461
62462
62463                 // Guess a type based on the tokens present in the template
62464                 // (This is for 'custom' source, where we don't know)
62465                 if (!source.type) {
62466                     if (/SERVICE=WMS|\{(proj|wkid|bbox)\}/.test(_template)) {
62467                         source.type = 'wms';
62468                         source.projection = 'EPSG:3857';  // guess
62469                     } else if (/\{(x|y)\}/.test(_template)) {
62470                         source.type = 'tms';
62471                     } else if (/\{u\}/.test(_template)) {
62472                         source.type = 'bing';
62473                     }
62474                 }
62475
62476
62477                 if (source.type === 'wms') {
62478                     var tileToProjectedCoords = (function(x, y, z) {
62479                         //polyfill for IE11, PhantomJS
62480                         var sinh = Math.sinh || function(x) {
62481                             var y = Math.exp(x);
62482                             return (y - 1 / y) / 2;
62483                         };
62484
62485                         var zoomSize = Math.pow(2, z);
62486                         var lon = x / zoomSize * Math.PI * 2 - Math.PI;
62487                         var lat = Math.atan(sinh(Math.PI * (1 - 2 * y / zoomSize)));
62488
62489                         switch (source.projection) {
62490                             case 'EPSG:4326':
62491                                 return {
62492                                     x: lon * 180 / Math.PI,
62493                                     y: lat * 180 / Math.PI
62494                                 };
62495                             default: // EPSG:3857 and synonyms
62496                                 var mercCoords = mercatorRaw(lon, lat);
62497                                 return {
62498                                     x: 20037508.34 / Math.PI * mercCoords[0],
62499                                     y: 20037508.34 / Math.PI * mercCoords[1]
62500                                 };
62501                         }
62502                     });
62503
62504                     var tileSize = source.tileSize;
62505                     var projection = source.projection;
62506                     var minXmaxY = tileToProjectedCoords(coord[0], coord[1], coord[2]);
62507                     var maxXminY = tileToProjectedCoords(coord[0]+1, coord[1]+1, coord[2]);
62508
62509                     result = result.replace(/\{(\w+)\}/g, function (token, key) {
62510                       switch (key) {
62511                         case 'width':
62512                         case 'height':
62513                             return tileSize;
62514                         case 'proj':
62515                             return projection;
62516                         case 'wkid':
62517                             return projection.replace(/^EPSG:/, '');
62518                         case 'bbox':
62519                             // WMS 1.3 flips x/y for some coordinate systems including EPSG:4326 - #7557
62520                             if (projection === 'EPSG:4326' &&
62521                                 // The CRS parameter implies version 1.3 (prior versions use SRS)
62522                                 /VERSION=1.3|CRS={proj}/.test(source.template())) {
62523                                 return maxXminY.y + ',' + minXmaxY.x + ',' + minXmaxY.y + ',' + maxXminY.x;
62524                             } else {
62525                                 return minXmaxY.x + ',' + maxXminY.y + ',' + maxXminY.x + ',' + minXmaxY.y;
62526                             }
62527                         case 'w':
62528                             return minXmaxY.x;
62529                         case 's':
62530                             return maxXminY.y;
62531                         case 'n':
62532                             return maxXminY.x;
62533                         case 'e':
62534                             return minXmaxY.y;
62535                         default:
62536                             return token;
62537                       }
62538                     });
62539
62540                 } else if (source.type === 'tms') {
62541                     result = result
62542                         .replace('{x}', coord[0])
62543                         .replace('{y}', coord[1])
62544                         // TMS-flipped y coordinate
62545                         .replace(/\{[t-]y\}/, Math.pow(2, coord[2]) - coord[1] - 1)
62546                         .replace(/\{z(oom)?\}/, coord[2])
62547                         // only fetch retina tiles for retina screens
62548                         .replace(/\{@2x\}|\{r\}/, isRetina ? '@2x' : '');
62549
62550                 } else if (source.type === 'bing') {
62551                     result = result
62552                         .replace('{u}', function() {
62553                             var u = '';
62554                             for (var zoom = coord[2]; zoom > 0; zoom--) {
62555                                 var b = 0;
62556                                 var mask = 1 << (zoom - 1);
62557                                 if ((coord[0] & mask) !== 0) { b++; }
62558                                 if ((coord[1] & mask) !== 0) { b += 2; }
62559                                 u += b.toString();
62560                             }
62561                             return u;
62562                         });
62563                 }
62564
62565                 // these apply to any type..
62566                 result = result.replace(/\{switch:([^}]+)\}/, function(s, r) {
62567                     var subdomains = r.split(',');
62568                     return subdomains[(coord[0] + coord[1]) % subdomains.length];
62569                 });
62570
62571
62572                 return result;
62573             };
62574
62575
62576             source.validZoom = function(z) {
62577                 return source.zoomExtent[0] <= z &&
62578                     (source.overzoom || source.zoomExtent[1] > z);
62579             };
62580
62581
62582             source.isLocatorOverlay = function() {
62583                 return source.id === 'mapbox_locator_overlay';
62584             };
62585
62586
62587             /* hides a source from the list, but leaves it available for use */
62588             source.isHidden = function() {
62589                 return source.id === 'DigitalGlobe-Premium-vintage' ||
62590                     source.id === 'DigitalGlobe-Standard-vintage';
62591             };
62592
62593
62594             source.copyrightNotices = function() {};
62595
62596
62597             source.getMetadata = function(center, tileCoord, callback) {
62598                 var vintage = {
62599                     start: localeDateString(source.startDate),
62600                     end: localeDateString(source.endDate)
62601                 };
62602                 vintage.range = vintageRange(vintage);
62603
62604                 var metadata = { vintage: vintage };
62605                 callback(null, metadata);
62606             };
62607
62608
62609             return source;
62610         }
62611
62612
62613         rendererBackgroundSource.Bing = function(data, dispatch) {
62614             // http://msdn.microsoft.com/en-us/library/ff701716.aspx
62615             // http://msdn.microsoft.com/en-us/library/ff701701.aspx
62616
62617             data.template = 'https://ecn.t{switch:0,1,2,3}.tiles.virtualearth.net/tiles/a{u}.jpeg?g=587&mkt=en-gb&n=z';
62618
62619             var bing = rendererBackgroundSource(data);
62620             // var key = 'Arzdiw4nlOJzRwOz__qailc8NiR31Tt51dN2D7cm57NrnceZnCpgOkmJhNpGoppU'; // P2, JOSM, etc
62621             var key = 'Ak5oTE46TUbjRp08OFVcGpkARErDobfpuyNKa-W2mQ8wbt1K1KL8p1bIRwWwcF-Q';    // iD
62622
62623
62624             var url = 'https://dev.virtualearth.net/REST/v1/Imagery/Metadata/Aerial?include=ImageryProviders&key=' + key;
62625             var cache = {};
62626             var inflight = {};
62627             var providers = [];
62628
62629             d3_json(url)
62630                 .then(function(json) {
62631                     providers = json.resourceSets[0].resources[0].imageryProviders.map(function(provider) {
62632                         return {
62633                             attribution: provider.attribution,
62634                             areas: provider.coverageAreas.map(function(area) {
62635                                 return {
62636                                     zoom: [area.zoomMin, area.zoomMax],
62637                                     extent: geoExtent([area.bbox[1], area.bbox[0]], [area.bbox[3], area.bbox[2]])
62638                                 };
62639                             })
62640                         };
62641                     });
62642                     dispatch.call('change');
62643                 })
62644                 .catch(function() {
62645                     /* ignore */
62646                 });
62647
62648
62649             bing.copyrightNotices = function(zoom, extent) {
62650                 zoom = Math.min(zoom, 21);
62651                 return providers.filter(function(provider) {
62652                     return provider.areas.some(function(area) {
62653                         return extent.intersects(area.extent) &&
62654                             area.zoom[0] <= zoom &&
62655                             area.zoom[1] >= zoom;
62656                     });
62657                 }).map(function(provider) {
62658                     return provider.attribution;
62659                 }).join(', ');
62660             };
62661
62662
62663             bing.getMetadata = function(center, tileCoord, callback) {
62664                 var tileID = tileCoord.slice(0, 3).join('/');
62665                 var zoom = Math.min(tileCoord[2], 21);
62666                 var centerPoint = center[1] + ',' + center[0];  // lat,lng
62667                 var url = 'https://dev.virtualearth.net/REST/v1/Imagery/Metadata/Aerial/' + centerPoint +
62668                         '?zl=' + zoom + '&key=' + key;
62669
62670                 if (inflight[tileID]) { return; }
62671
62672                 if (!cache[tileID]) {
62673                     cache[tileID] = {};
62674                 }
62675                 if (cache[tileID] && cache[tileID].metadata) {
62676                     return callback(null, cache[tileID].metadata);
62677                 }
62678
62679                 inflight[tileID] = true;
62680                 d3_json(url)
62681                     .then(function(result) {
62682                         delete inflight[tileID];
62683                         if (!result) {
62684                             throw new Error('Unknown Error');
62685                         }
62686                         var vintage = {
62687                             start: localeDateString(result.resourceSets[0].resources[0].vintageStart),
62688                             end: localeDateString(result.resourceSets[0].resources[0].vintageEnd)
62689                         };
62690                         vintage.range = vintageRange(vintage);
62691
62692                         var metadata = { vintage: vintage };
62693                         cache[tileID].metadata = metadata;
62694                         if (callback) { callback(null, metadata); }
62695                     })
62696                     .catch(function(err) {
62697                         delete inflight[tileID];
62698                         if (callback) { callback(err.message); }
62699                     });
62700             };
62701
62702
62703             bing.terms_url = 'https://blog.openstreetmap.org/2010/11/30/microsoft-imagery-details';
62704
62705
62706             return bing;
62707         };
62708
62709
62710
62711         rendererBackgroundSource.Esri = function(data) {
62712             // in addition to using the tilemap at zoom level 20, overzoom real tiles - #4327 (deprecated technique, but it works)
62713             if (data.template.match(/blankTile/) === null) {
62714                 data.template = data.template + '?blankTile=false';
62715             }
62716
62717             var esri = rendererBackgroundSource(data);
62718             var cache = {};
62719             var inflight = {};
62720             var _prevCenter;
62721
62722             // use a tilemap service to set maximum zoom for esri tiles dynamically
62723             // https://developers.arcgis.com/documentation/tiled-elevation-service/
62724             esri.fetchTilemap = function(center) {
62725                 // skip if we have already fetched a tilemap within 5km
62726                 if (_prevCenter && geoSphericalDistance(center, _prevCenter) < 5000) { return; }
62727                 _prevCenter = center;
62728
62729                 // tiles are available globally to zoom level 19, afterward they may or may not be present
62730                 var z = 20;
62731
62732                 // first generate a random url using the template
62733                 var dummyUrl = esri.url([1,2,3]);
62734
62735                 // calculate url z/y/x from the lat/long of the center of the map
62736                 var x = (Math.floor((center[0] + 180) / 360 * Math.pow(2, z)));
62737                 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)));
62738
62739                 // fetch an 8x8 grid to leverage cache
62740                 var tilemapUrl = dummyUrl.replace(/tile\/[0-9]+\/[0-9]+\/[0-9]+\?blankTile=false/, 'tilemap') + '/' + z + '/' + y + '/' + x + '/8/8';
62741
62742                 // make the request and introspect the response from the tilemap server
62743                 d3_json(tilemapUrl)
62744                     .then(function(tilemap) {
62745                         if (!tilemap) {
62746                             throw new Error('Unknown Error');
62747                         }
62748                         var hasTiles = true;
62749                         for (var i = 0; i < tilemap.data.length; i++) {
62750                             // 0 means an individual tile in the grid doesn't exist
62751                             if (!tilemap.data[i]) {
62752                                 hasTiles = false;
62753                                 break;
62754                             }
62755                         }
62756
62757                         // if any tiles are missing at level 20 we restrict maxZoom to 19
62758                         esri.zoomExtent[1] = (hasTiles ? 22 : 19);
62759                     })
62760                     .catch(function() {
62761                         /* ignore */
62762                     });
62763             };
62764
62765
62766             esri.getMetadata = function(center, tileCoord, callback) {
62767                 var tileID = tileCoord.slice(0, 3).join('/');
62768                 var zoom = Math.min(tileCoord[2], esri.zoomExtent[1]);
62769                 var centerPoint = center[0] + ',' + center[1];  // long, lat (as it should be)
62770                 var unknown = _t('info_panels.background.unknown');
62771                 var metadataLayer;
62772                 var vintage = {};
62773                 var metadata = {};
62774
62775                 if (inflight[tileID]) { return; }
62776
62777                 switch (true) {
62778                     case (zoom >= 20 && esri.id === 'EsriWorldImageryClarity'):
62779                         metadataLayer = 4;
62780                         break;
62781                     case zoom >= 19:
62782                         metadataLayer = 3;
62783                         break;
62784                     case zoom >= 17:
62785                         metadataLayer = 2;
62786                         break;
62787                     case zoom >= 13:
62788                         metadataLayer = 0;
62789                         break;
62790                     default:
62791                         metadataLayer = 99;
62792                 }
62793
62794                 var url;
62795                 // build up query using the layer appropriate to the current zoom
62796                 if (esri.id === 'EsriWorldImagery') {
62797                     url = 'https://services.arcgisonline.com/arcgis/rest/services/World_Imagery/MapServer/';
62798                 } else if (esri.id === 'EsriWorldImageryClarity') {
62799                     url = 'https://serviceslab.arcgisonline.com/arcgis/rest/services/Clarity_World_Imagery/MapServer/';
62800                 }
62801
62802                 url += metadataLayer + '/query?returnGeometry=false&geometry=' + centerPoint + '&inSR=4326&geometryType=esriGeometryPoint&outFields=*&f=json';
62803
62804                 if (!cache[tileID]) {
62805                     cache[tileID] = {};
62806                 }
62807                 if (cache[tileID] && cache[tileID].metadata) {
62808                     return callback(null, cache[tileID].metadata);
62809                 }
62810
62811                 // accurate metadata is only available >= 13
62812                 if (metadataLayer === 99) {
62813                     vintage = {
62814                         start: null,
62815                         end: null,
62816                         range: null
62817                     };
62818                     metadata = {
62819                         vintage: null,
62820                         source: unknown,
62821                         description: unknown,
62822                         resolution: unknown,
62823                         accuracy: unknown
62824                     };
62825
62826                     callback(null, metadata);
62827
62828                 } else {
62829                     inflight[tileID] = true;
62830                     d3_json(url)
62831                         .then(function(result) {
62832                             delete inflight[tileID];
62833                             if (!result) {
62834                                 throw new Error('Unknown Error');
62835                             } else if (result.features && result.features.length < 1) {
62836                                 throw new Error('No Results');
62837                             } else if (result.error && result.error.message) {
62838                                 throw new Error(result.error.message);
62839                             }
62840
62841                             // pass through the discrete capture date from metadata
62842                             var captureDate = localeDateString(result.features[0].attributes.SRC_DATE2);
62843                             vintage = {
62844                                 start: captureDate,
62845                                 end: captureDate,
62846                                 range: captureDate
62847                             };
62848                             metadata = {
62849                                 vintage: vintage,
62850                                 source: clean(result.features[0].attributes.NICE_NAME),
62851                                 description: clean(result.features[0].attributes.NICE_DESC),
62852                                 resolution: clean(+parseFloat(result.features[0].attributes.SRC_RES).toFixed(4)),
62853                                 accuracy: clean(+parseFloat(result.features[0].attributes.SRC_ACC).toFixed(4))
62854                             };
62855
62856                             // append units - meters
62857                             if (isFinite(metadata.resolution)) {
62858                                 metadata.resolution += ' m';
62859                             }
62860                             if (isFinite(metadata.accuracy)) {
62861                                 metadata.accuracy += ' m';
62862                             }
62863
62864                             cache[tileID].metadata = metadata;
62865                             if (callback) { callback(null, metadata); }
62866                         })
62867                         .catch(function(err) {
62868                             delete inflight[tileID];
62869                             if (callback) { callback(err.message); }
62870                         });
62871                 }
62872
62873
62874                 function clean(val) {
62875                     return String(val).trim() || unknown;
62876                 }
62877             };
62878
62879             return esri;
62880         };
62881
62882
62883         rendererBackgroundSource.None = function() {
62884             var source = rendererBackgroundSource({ id: 'none', template: '' });
62885
62886
62887             source.name = function() {
62888                 return _t('background.none');
62889             };
62890
62891
62892             source.imageryUsed = function() {
62893                 return null;
62894             };
62895
62896
62897             source.area = function() {
62898                 return -1;  // sources in background pane are sorted by area
62899             };
62900
62901
62902             return source;
62903         };
62904
62905
62906         rendererBackgroundSource.Custom = function(template) {
62907             var source = rendererBackgroundSource({ id: 'custom', template: template });
62908
62909
62910             source.name = function() {
62911                 return _t('background.custom');
62912             };
62913
62914
62915             source.imageryUsed = function() {
62916                 // sanitize personal connection tokens - #6801
62917                 var cleaned = source.template();
62918
62919                 // from query string parameters
62920                 if (cleaned.indexOf('?') !== -1) {
62921                     var parts = cleaned.split('?', 2);
62922                     var qs = utilStringQs(parts[1]);
62923
62924                     ['access_token', 'connectId', 'token'].forEach(function(param) {
62925                         if (qs[param]) {
62926                             qs[param] = '{apikey}';
62927                         }
62928                     });
62929                     cleaned = parts[0] + '?' + utilQsString(qs, true);  // true = soft encode
62930                 }
62931
62932                 // from wms/wmts api path parameters
62933                 cleaned = cleaned.replace(/token\/(\w+)/, 'token/{apikey}');
62934
62935                 return 'Custom (' + cleaned + ' )';
62936             };
62937
62938
62939             source.area = function() {
62940                 return -2;  // sources in background pane are sorted by area
62941             };
62942
62943
62944             return source;
62945         };
62946
62947         function rendererTileLayer(context) {
62948             var transformProp = utilPrefixCSSProperty('Transform');
62949             var tiler = utilTiler();
62950
62951             var _tileSize = 256;
62952             var _projection;
62953             var _cache = {};
62954             var _tileOrigin;
62955             var _zoom;
62956             var _source;
62957
62958
62959             function tileSizeAtZoom(d, z) {
62960                 var EPSILON = 0.002;    // close seams
62961                 return ((_tileSize * Math.pow(2, z - d[2])) / _tileSize) + EPSILON;
62962             }
62963
62964
62965             function atZoom(t, distance) {
62966                 var power = Math.pow(2, distance);
62967                 return [
62968                     Math.floor(t[0] * power),
62969                     Math.floor(t[1] * power),
62970                     t[2] + distance
62971                 ];
62972             }
62973
62974
62975             function lookUp(d) {
62976                 for (var up = -1; up > -d[2]; up--) {
62977                     var tile = atZoom(d, up);
62978                     if (_cache[_source.url(tile)] !== false) {
62979                         return tile;
62980                     }
62981                 }
62982             }
62983
62984
62985             function uniqueBy(a, n) {
62986                 var o = [];
62987                 var seen = {};
62988                 for (var i = 0; i < a.length; i++) {
62989                     if (seen[a[i][n]] === undefined) {
62990                         o.push(a[i]);
62991                         seen[a[i][n]] = true;
62992                     }
62993                 }
62994                 return o;
62995             }
62996
62997
62998             function addSource(d) {
62999                 d.push(_source.url(d));
63000                 return d;
63001             }
63002
63003
63004             // Update tiles based on current state of `projection`.
63005             function background(selection) {
63006                 _zoom = geoScaleToZoom(_projection.scale(), _tileSize);
63007
63008                 var pixelOffset;
63009                 if (_source) {
63010                     pixelOffset = [
63011                         _source.offset()[0] * Math.pow(2, _zoom),
63012                         _source.offset()[1] * Math.pow(2, _zoom)
63013                     ];
63014                 } else {
63015                     pixelOffset = [0, 0];
63016                 }
63017
63018                 var translate = [
63019                     _projection.translate()[0] + pixelOffset[0],
63020                     _projection.translate()[1] + pixelOffset[1]
63021                 ];
63022
63023                 tiler
63024                     .scale(_projection.scale() * 2 * Math.PI)
63025                     .translate(translate);
63026
63027                 _tileOrigin = [
63028                     _projection.scale() * Math.PI - translate[0],
63029                     _projection.scale() * Math.PI - translate[1]
63030                 ];
63031
63032                 render(selection);
63033             }
63034
63035
63036             // Derive the tiles onscreen, remove those offscreen and position them.
63037             // Important that this part not depend on `_projection` because it's
63038             // rentered when tiles load/error (see #644).
63039             function render(selection) {
63040                 if (!_source) { return; }
63041                 var requests = [];
63042                 var showDebug = context.getDebug('tile') && !_source.overlay;
63043
63044                 if (_source.validZoom(_zoom)) {
63045                     tiler.skipNullIsland(!!_source.overlay);
63046
63047                     tiler().forEach(function(d) {
63048                         addSource(d);
63049                         if (d[3] === '') { return; }
63050                         if (typeof d[3] !== 'string') { return; } // Workaround for #2295
63051                         requests.push(d);
63052                         if (_cache[d[3]] === false && lookUp(d)) {
63053                             requests.push(addSource(lookUp(d)));
63054                         }
63055                     });
63056
63057                     requests = uniqueBy(requests, 3).filter(function(r) {
63058                         // don't re-request tiles which have failed in the past
63059                         return _cache[r[3]] !== false;
63060                     });
63061                 }
63062
63063                 function load(d) {
63064                     _cache[d[3]] = true;
63065                     select(this)
63066                         .on('error', null)
63067                         .on('load', null)
63068                         .classed('tile-loaded', true);
63069                     render(selection);
63070                 }
63071
63072                 function error(d) {
63073                     _cache[d[3]] = false;
63074                     select(this)
63075                         .on('error', null)
63076                         .on('load', null)
63077                         .remove();
63078                     render(selection);
63079                 }
63080
63081                 function imageTransform(d) {
63082                     var ts = _tileSize * Math.pow(2, _zoom - d[2]);
63083                     var scale = tileSizeAtZoom(d, _zoom);
63084                     return 'translate(' +
63085                         ((d[0] * ts) - _tileOrigin[0]) + 'px,' +
63086                         ((d[1] * ts) - _tileOrigin[1]) + 'px) ' +
63087                         'scale(' + scale + ',' + scale + ')';
63088                 }
63089
63090                 function tileCenter(d) {
63091                     var ts = _tileSize * Math.pow(2, _zoom - d[2]);
63092                     return [
63093                         ((d[0] * ts) - _tileOrigin[0] + (ts / 2)),
63094                         ((d[1] * ts) - _tileOrigin[1] + (ts / 2))
63095                     ];
63096                 }
63097
63098                 function debugTransform(d) {
63099                     var coord = tileCenter(d);
63100                     return 'translate(' + coord[0] + 'px,' + coord[1] + 'px)';
63101                 }
63102
63103
63104                 // Pick a representative tile near the center of the viewport
63105                 // (This is useful for sampling the imagery vintage)
63106                 var dims = tiler.size();
63107                 var mapCenter = [dims[0] / 2, dims[1] / 2];
63108                 var minDist = Math.max(dims[0], dims[1]);
63109                 var nearCenter;
63110
63111                 requests.forEach(function(d) {
63112                     var c = tileCenter(d);
63113                     var dist = geoVecLength(c, mapCenter);
63114                     if (dist < minDist) {
63115                         minDist = dist;
63116                         nearCenter = d;
63117                     }
63118                 });
63119
63120
63121                 var image = selection.selectAll('img')
63122                     .data(requests, function(d) { return d[3]; });
63123
63124                 image.exit()
63125                     .style(transformProp, imageTransform)
63126                     .classed('tile-removing', true)
63127                     .classed('tile-center', false)
63128                     .each(function() {
63129                         var tile = select(this);
63130                         window.setTimeout(function() {
63131                             if (tile.classed('tile-removing')) {
63132                                 tile.remove();
63133                             }
63134                         }, 300);
63135                     });
63136
63137                 image.enter()
63138                   .append('img')
63139                     .attr('class', 'tile')
63140                     .attr('draggable', 'false')
63141                     .style('width', _tileSize + 'px')
63142                     .style('height', _tileSize + 'px')
63143                     .attr('src', function(d) { return d[3]; })
63144                     .on('error', error)
63145                     .on('load', load)
63146                   .merge(image)
63147                     .style(transformProp, imageTransform)
63148                     .classed('tile-debug', showDebug)
63149                     .classed('tile-removing', false)
63150                     .classed('tile-center', function(d) { return d === nearCenter; });
63151
63152
63153
63154                 var debug = selection.selectAll('.tile-label-debug')
63155                     .data(showDebug ? requests : [], function(d) { return d[3]; });
63156
63157                 debug.exit()
63158                     .remove();
63159
63160                 if (showDebug) {
63161                     var debugEnter = debug.enter()
63162                         .append('div')
63163                         .attr('class', 'tile-label-debug');
63164
63165                     debugEnter
63166                         .append('div')
63167                         .attr('class', 'tile-label-debug-coord');
63168
63169                     debugEnter
63170                         .append('div')
63171                         .attr('class', 'tile-label-debug-vintage');
63172
63173                     debug = debug.merge(debugEnter);
63174
63175                     debug
63176                         .style(transformProp, debugTransform);
63177
63178                     debug
63179                         .selectAll('.tile-label-debug-coord')
63180                         .text(function(d) { return d[2] + ' / ' + d[0] + ' / ' + d[1]; });
63181
63182                     debug
63183                         .selectAll('.tile-label-debug-vintage')
63184                         .each(function(d) {
63185                             var span = select(this);
63186                             var center = context.projection.invert(tileCenter(d));
63187                             _source.getMetadata(center, d, function(err, result) {
63188                                 span.text((result && result.vintage && result.vintage.range) ||
63189                                     _t('info_panels.background.vintage') + ': ' + _t('info_panels.background.unknown')
63190                                 );
63191                             });
63192                         });
63193                 }
63194
63195             }
63196
63197
63198             background.projection = function(val) {
63199                 if (!arguments.length) { return _projection; }
63200                 _projection = val;
63201                 return background;
63202             };
63203
63204
63205             background.dimensions = function(val) {
63206                 if (!arguments.length) { return tiler.size(); }
63207                 tiler.size(val);
63208                 return background;
63209             };
63210
63211
63212             background.source = function(val) {
63213                 if (!arguments.length) { return _source; }
63214                 _source = val;
63215                 _tileSize = _source.tileSize;
63216                 _cache = {};
63217                 tiler.tileSize(_source.tileSize).zoomExtent(_source.zoomExtent);
63218                 return background;
63219             };
63220
63221
63222             return background;
63223         }
63224
63225         var _imageryIndex = null;
63226
63227         function rendererBackground(context) {
63228           var dispatch$1 = dispatch('change');
63229           var detected = utilDetect();
63230           var baseLayer = rendererTileLayer(context).projection(context.projection);
63231           var _isValid = true;
63232           var _overlayLayers = [];
63233           var _brightness = 1;
63234           var _contrast = 1;
63235           var _saturation = 1;
63236           var _sharpness = 1;
63237
63238
63239           function ensureImageryIndex() {
63240             return _mainFileFetcher.get('imagery')
63241               .then(function (sources) {
63242                 if (_imageryIndex) { return _imageryIndex; }
63243
63244                 _imageryIndex = {
63245                   imagery: sources,
63246                   features: {}
63247                 };
63248
63249                 // use which-polygon to support efficient index and querying for imagery
63250                 var features = sources.map(function (source) {
63251                   if (!source.polygon) { return null; }
63252                   // workaround for editor-layer-index weirdness..
63253                   // Add an extra array nest to each element in `source.polygon`
63254                   // so the rings are not treated as a bunch of holes:
63255                   // what we have: [ [[outer],[hole],[hole]] ]
63256                   // what we want: [ [[outer]],[[outer]],[[outer]] ]
63257                   var rings = source.polygon.map(function (ring) { return [ring]; });
63258
63259                   var feature = {
63260                     type: 'Feature',
63261                     properties: { id: source.id },
63262                     geometry: { type: 'MultiPolygon', coordinates: rings }
63263                   };
63264
63265                   _imageryIndex.features[source.id] = feature;
63266                   return feature;
63267
63268                 }).filter(Boolean);
63269
63270                 _imageryIndex.query = whichPolygon_1({ type: 'FeatureCollection', features: features });
63271
63272
63273                 // Instantiate `rendererBackgroundSource` objects for each source
63274                 _imageryIndex.backgrounds = sources.map(function (source) {
63275                   if (source.type === 'bing') {
63276                     return rendererBackgroundSource.Bing(source, dispatch$1);
63277                   } else if (/^EsriWorldImagery/.test(source.id)) {
63278                     return rendererBackgroundSource.Esri(source);
63279                   } else {
63280                     return rendererBackgroundSource(source);
63281                   }
63282                 });
63283
63284                 // Add 'None'
63285                 _imageryIndex.backgrounds.unshift(rendererBackgroundSource.None());
63286
63287                 // Add 'Custom'
63288                 var template = corePreferences('background-custom-template') || '';
63289                 var custom = rendererBackgroundSource.Custom(template);
63290                 _imageryIndex.backgrounds.unshift(custom);
63291
63292                 return _imageryIndex;
63293               });
63294           }
63295
63296
63297           function background(selection) {
63298             var currSource = baseLayer.source();
63299
63300             // If we are displaying an Esri basemap at high zoom,
63301             // check its tilemap to see how high the zoom can go
63302             if (context.map().zoom() > 18) {
63303               if (currSource && /^EsriWorldImagery/.test(currSource.id)) {
63304                 var center = context.map().center();
63305                 currSource.fetchTilemap(center);
63306               }
63307             }
63308
63309             // Is the imagery valid here? - #4827
63310             var sources = background.sources(context.map().extent());
63311             var wasValid = _isValid;
63312             _isValid = !!sources.filter(function (d) { return d === currSource; }).length;
63313
63314             if (wasValid !== _isValid) {      // change in valid status
63315               background.updateImagery();
63316             }
63317
63318
63319             var baseFilter = '';
63320             if (detected.cssfilters) {
63321               if (_brightness !== 1) {
63322                 baseFilter += " brightness(" + _brightness + ")";
63323               }
63324               if (_contrast !== 1) {
63325                 baseFilter += " contrast(" + _contrast + ")";
63326               }
63327               if (_saturation !== 1) {
63328                 baseFilter += " saturate(" + _saturation + ")";
63329               }
63330               if (_sharpness < 1) {  // gaussian blur
63331                 var blur = d3_interpolateNumber(0.5, 5)(1 - _sharpness);
63332                 baseFilter += " blur(" + blur + "px)";
63333               }
63334             }
63335
63336             var base = selection.selectAll('.layer-background')
63337               .data([0]);
63338
63339             base = base.enter()
63340               .insert('div', '.layer-data')
63341               .attr('class', 'layer layer-background')
63342               .merge(base);
63343
63344             if (detected.cssfilters) {
63345               base.style('filter', baseFilter || null);
63346             } else {
63347               base.style('opacity', _brightness);
63348             }
63349
63350
63351             var imagery = base.selectAll('.layer-imagery')
63352               .data([0]);
63353
63354             imagery.enter()
63355               .append('div')
63356               .attr('class', 'layer layer-imagery')
63357               .merge(imagery)
63358               .call(baseLayer);
63359
63360
63361             var maskFilter = '';
63362             var mixBlendMode = '';
63363             if (detected.cssfilters && _sharpness > 1) {  // apply unsharp mask
63364               mixBlendMode = 'overlay';
63365               maskFilter = 'saturate(0) blur(3px) invert(1)';
63366
63367               var contrast = _sharpness - 1;
63368               maskFilter += " contrast(" + contrast + ")";
63369
63370               var brightness = d3_interpolateNumber(1, 0.85)(_sharpness - 1);
63371               maskFilter += " brightness(" + brightness + ")";
63372             }
63373
63374             var mask = base.selectAll('.layer-unsharp-mask')
63375               .data(detected.cssfilters && _sharpness > 1 ? [0] : []);
63376
63377             mask.exit()
63378               .remove();
63379
63380             mask.enter()
63381               .append('div')
63382               .attr('class', 'layer layer-mask layer-unsharp-mask')
63383               .merge(mask)
63384               .call(baseLayer)
63385               .style('filter', maskFilter || null)
63386               .style('mix-blend-mode', mixBlendMode || null);
63387
63388
63389             var overlays = selection.selectAll('.layer-overlay')
63390               .data(_overlayLayers, function (d) { return d.source().name(); });
63391
63392             overlays.exit()
63393               .remove();
63394
63395             overlays.enter()
63396               .insert('div', '.layer-data')
63397               .attr('class', 'layer layer-overlay')
63398               .merge(overlays)
63399               .each(function (layer, i, nodes) { return select(nodes[i]).call(layer); });
63400           }
63401
63402
63403           background.updateImagery = function() {
63404             var currSource = baseLayer.source();
63405             if (context.inIntro() || !currSource) { return; }
63406
63407             var o = _overlayLayers
63408               .filter(function (d) { return !d.source().isLocatorOverlay() && !d.source().isHidden(); })
63409               .map(function (d) { return d.source().id; })
63410               .join(',');
63411
63412             var meters = geoOffsetToMeters(currSource.offset());
63413             var EPSILON = 0.01;
63414             var x = +meters[0].toFixed(2);
63415             var y = +meters[1].toFixed(2);
63416             var hash = utilStringQs(window.location.hash);
63417
63418             var id = currSource.id;
63419             if (id === 'custom') {
63420               id = "custom:" + (currSource.template());
63421             }
63422
63423             if (id) {
63424               hash.background = id;
63425             } else {
63426               delete hash.background;
63427             }
63428
63429             if (o) {
63430               hash.overlays = o;
63431             } else {
63432               delete hash.overlays;
63433             }
63434
63435             if (Math.abs(x) > EPSILON || Math.abs(y) > EPSILON) {
63436               hash.offset = x + "," + y;
63437             } else {
63438               delete hash.offset;
63439             }
63440
63441             if (!window.mocha) {
63442               window.location.replace('#' + utilQsString(hash, true));
63443             }
63444
63445             var imageryUsed = [];
63446             var photoOverlaysUsed = [];
63447
63448             var currUsed = currSource.imageryUsed();
63449             if (currUsed && _isValid) {
63450               imageryUsed.push(currUsed);
63451             }
63452
63453             _overlayLayers
63454               .filter(function (d) { return !d.source().isLocatorOverlay() && !d.source().isHidden(); })
63455               .forEach(function (d) { return imageryUsed.push(d.source().imageryUsed()); });
63456
63457             var dataLayer = context.layers().layer('data');
63458             if (dataLayer && dataLayer.enabled() && dataLayer.hasData()) {
63459               imageryUsed.push(dataLayer.getSrc());
63460             }
63461
63462             var photoOverlayLayers = {
63463               streetside: 'Bing Streetside',
63464               mapillary: 'Mapillary Images',
63465               'mapillary-map-features': 'Mapillary Map Features',
63466               'mapillary-signs': 'Mapillary Signs',
63467               openstreetcam: 'OpenStreetCam Images'
63468             };
63469
63470             for (var layerID in photoOverlayLayers) {
63471               var layer = context.layers().layer(layerID);
63472               if (layer && layer.enabled()) {
63473                 photoOverlaysUsed.push(layerID);
63474                 imageryUsed.push(photoOverlayLayers[layerID]);
63475               }
63476             }
63477
63478             context.history().imageryUsed(imageryUsed);
63479             context.history().photoOverlaysUsed(photoOverlaysUsed);
63480           };
63481
63482           var _checkedBlocklists;
63483
63484           background.sources = function (extent, zoom, includeCurrent) {
63485             if (!_imageryIndex) { return []; }   // called before init()?
63486
63487             var visible = {};
63488             (_imageryIndex.query.bbox(extent.rectangle(), true) || [])
63489               .forEach(function (d) { return visible[d.id] = true; });
63490
63491             var currSource = baseLayer.source();
63492
63493             var osm = context.connection();
63494             var blocklists = osm && osm.imageryBlocklists();
63495
63496             if (blocklists && blocklists !== _checkedBlocklists) {
63497               _imageryIndex.backgrounds.forEach(function (source) {
63498                 source.isBlocked = blocklists.some(function(blocklist) {
63499                   return blocklist.test(source.template());
63500                 });
63501               });
63502               _checkedBlocklists = blocklists;
63503             }
63504
63505             return _imageryIndex.backgrounds.filter(function (source) {
63506               if (includeCurrent && currSource === source) { return true; }  // optionally always include the current imagery
63507               if (source.isBlocked) { return false; }                        // even bundled sources may be blocked - #7905
63508               if (!source.polygon) { return true; }                          // always include imagery with worldwide coverage
63509               if (zoom && zoom < 6) { return false; }                        // optionally exclude local imagery at low zooms
63510               return visible[source.id];                                 // include imagery visible in given extent
63511             });
63512           };
63513
63514
63515           background.dimensions = function (val) {
63516             if (!val) { return; }
63517             baseLayer.dimensions(val);
63518             _overlayLayers.forEach(function (layer) { return layer.dimensions(val); });
63519           };
63520
63521
63522           background.baseLayerSource = function(d) {
63523             if (!arguments.length) { return baseLayer.source(); }
63524
63525             // test source against OSM imagery blocklists..
63526             var osm = context.connection();
63527             if (!osm) { return background; }
63528
63529             var blocklists = osm.imageryBlocklists();
63530             var template = d.template();
63531             var fail = false;
63532             var tested = 0;
63533             var regex;
63534
63535             for (var i = 0; i < blocklists.length; i++) {
63536               regex = blocklists[i];
63537               fail = regex.test(template);
63538               tested++;
63539               if (fail) { break; }
63540             }
63541
63542             // ensure at least one test was run.
63543             if (!tested) {
63544               regex = /.*\.google(apis)?\..*\/(vt|kh)[\?\/].*([xyz]=.*){3}.*/;
63545               fail = regex.test(template);
63546             }
63547
63548             baseLayer.source(!fail ? d : background.findSource('none'));
63549             dispatch$1.call('change');
63550             background.updateImagery();
63551             return background;
63552           };
63553
63554
63555           background.findSource = function (id) {
63556             if (!id || !_imageryIndex) { return null; }   // called before init()?
63557             return _imageryIndex.backgrounds.find(function (d) { return d.id && d.id === id; });
63558           };
63559
63560
63561           background.bing = function () {
63562             background.baseLayerSource(background.findSource('Bing'));
63563           };
63564
63565
63566           background.showsLayer = function (d) {
63567             var currSource = baseLayer.source();
63568             if (!d || !currSource) { return false; }
63569             return d.id === currSource.id || _overlayLayers.some(function (layer) { return d.id === layer.source().id; });
63570           };
63571
63572
63573           background.overlayLayerSources = function () {
63574             return _overlayLayers.map(function (layer) { return layer.source(); });
63575           };
63576
63577
63578           background.toggleOverlayLayer = function (d) {
63579             var layer;
63580             for (var i = 0; i < _overlayLayers.length; i++) {
63581               layer = _overlayLayers[i];
63582               if (layer.source() === d) {
63583                 _overlayLayers.splice(i, 1);
63584                 dispatch$1.call('change');
63585                 background.updateImagery();
63586                 return;
63587               }
63588             }
63589
63590             layer = rendererTileLayer(context)
63591               .source(d)
63592               .projection(context.projection)
63593               .dimensions(baseLayer.dimensions()
63594             );
63595
63596             _overlayLayers.push(layer);
63597             dispatch$1.call('change');
63598             background.updateImagery();
63599           };
63600
63601
63602           background.nudge = function (d, zoom) {
63603             var currSource = baseLayer.source();
63604             if (currSource) {
63605               currSource.nudge(d, zoom);
63606               dispatch$1.call('change');
63607               background.updateImagery();
63608             }
63609             return background;
63610           };
63611
63612
63613           background.offset = function(d) {
63614             var currSource = baseLayer.source();
63615             if (!arguments.length) {
63616               return (currSource && currSource.offset()) || [0, 0];
63617             }
63618             if (currSource) {
63619               currSource.offset(d);
63620               dispatch$1.call('change');
63621               background.updateImagery();
63622             }
63623             return background;
63624           };
63625
63626
63627           background.brightness = function(d) {
63628             if (!arguments.length) { return _brightness; }
63629             _brightness = d;
63630             if (context.mode()) { dispatch$1.call('change'); }
63631             return background;
63632           };
63633
63634
63635           background.contrast = function(d) {
63636             if (!arguments.length) { return _contrast; }
63637             _contrast = d;
63638             if (context.mode()) { dispatch$1.call('change'); }
63639             return background;
63640           };
63641
63642
63643           background.saturation = function(d) {
63644             if (!arguments.length) { return _saturation; }
63645             _saturation = d;
63646             if (context.mode()) { dispatch$1.call('change'); }
63647             return background;
63648           };
63649
63650
63651           background.sharpness = function(d) {
63652             if (!arguments.length) { return _sharpness; }
63653             _sharpness = d;
63654             if (context.mode()) { dispatch$1.call('change'); }
63655             return background;
63656           };
63657
63658           var _loadPromise;
63659
63660           background.ensureLoaded = function () {
63661
63662             if (_loadPromise) { return _loadPromise; }
63663
63664             function parseMapParams(qmap) {
63665               if (!qmap) { return false; }
63666               var params = qmap.split('/').map(Number);
63667               if (params.length < 3 || params.some(isNaN)) { return false; }
63668               return geoExtent([params[2], params[1]]);  // lon,lat
63669             }
63670
63671             var hash = utilStringQs(window.location.hash);
63672             var requested = hash.background || hash.layer;
63673             var extent = parseMapParams(hash.map);
63674
63675             return _loadPromise = ensureImageryIndex()
63676               .then(function (imageryIndex) {
63677                 var first = imageryIndex.backgrounds.length && imageryIndex.backgrounds[0];
63678
63679                 var best;
63680                 if (!requested && extent) {
63681                   best = background.sources(extent).find(function (s) { return s.best(); });
63682                 }
63683
63684                 // Decide which background layer to display
63685                 if (requested && requested.indexOf('custom:') === 0) {
63686                   var template = requested.replace(/^custom:/, '');
63687                   var custom = background.findSource('custom');
63688                   background.baseLayerSource(custom.template(template));
63689                   corePreferences('background-custom-template', template);
63690                 } else {
63691                   background.baseLayerSource(
63692                     background.findSource(requested) ||
63693                     best ||
63694                     background.findSource(corePreferences('background-last-used')) ||
63695                     background.findSource('Bing') ||
63696                     first ||
63697                     background.findSource('none')
63698                   );
63699                 }
63700
63701                 var locator = imageryIndex.backgrounds.find(function (d) { return d.overlay && d.default; });
63702                 if (locator) {
63703                   background.toggleOverlayLayer(locator);
63704                 }
63705
63706                 var overlays = (hash.overlays || '').split(',');
63707                 overlays.forEach(function (overlay) {
63708                   overlay = background.findSource(overlay);
63709                   if (overlay) {
63710                     background.toggleOverlayLayer(overlay);
63711                   }
63712                 });
63713
63714                 if (hash.gpx) {
63715                   var gpx = context.layers().layer('data');
63716                   if (gpx) {
63717                     gpx.url(hash.gpx, '.gpx');
63718                   }
63719                 }
63720
63721                 if (hash.offset) {
63722                   var offset = hash.offset
63723                     .replace(/;/g, ',')
63724                     .split(',')
63725                     .map(function (n) { return !isNaN(n) && n; });
63726
63727                   if (offset.length === 2) {
63728                     background.offset(geoMetersToOffset(offset));
63729                   }
63730                 }
63731               })
63732               .catch(function () { /* ignore */ });
63733           };
63734
63735
63736           return utilRebind(background, dispatch$1, 'on');
63737         }
63738
63739         function rendererFeatures(context) {
63740             var dispatch$1 = dispatch('change', 'redraw');
63741             var features = utilRebind({}, dispatch$1, 'on');
63742             var _deferred = new Set();
63743
63744             var traffic_roads = {
63745                 'motorway': true,
63746                 'motorway_link': true,
63747                 'trunk': true,
63748                 'trunk_link': true,
63749                 'primary': true,
63750                 'primary_link': true,
63751                 'secondary': true,
63752                 'secondary_link': true,
63753                 'tertiary': true,
63754                 'tertiary_link': true,
63755                 'residential': true,
63756                 'unclassified': true,
63757                 'living_street': true
63758             };
63759
63760             var service_roads = {
63761                 'service': true,
63762                 'road': true,
63763                 'track': true
63764             };
63765
63766             var paths = {
63767                 'path': true,
63768                 'footway': true,
63769                 'cycleway': true,
63770                 'bridleway': true,
63771                 'steps': true,
63772                 'pedestrian': true
63773             };
63774
63775             var past_futures = {
63776                 'proposed': true,
63777                 'construction': true,
63778                 'abandoned': true,
63779                 'dismantled': true,
63780                 'disused': true,
63781                 'razed': true,
63782                 'demolished': true,
63783                 'obliterated': true
63784             };
63785
63786             var _cullFactor = 1;
63787             var _cache = {};
63788             var _rules = {};
63789             var _stats = {};
63790             var _keys = [];
63791             var _hidden = [];
63792             var _forceVisible = {};
63793
63794
63795             function update() {
63796                 if (!window.mocha) {
63797                     var hash = utilStringQs(window.location.hash);
63798                     var disabled = features.disabled();
63799                     if (disabled.length) {
63800                         hash.disable_features = disabled.join(',');
63801                     } else {
63802                         delete hash.disable_features;
63803                     }
63804                     window.location.replace('#' + utilQsString(hash, true));
63805                     corePreferences('disabled-features', disabled.join(','));
63806                 }
63807                 _hidden = features.hidden();
63808                 dispatch$1.call('change');
63809                 dispatch$1.call('redraw');
63810             }
63811
63812
63813             function defineRule(k, filter, max) {
63814                 var isEnabled = true;
63815
63816                 _keys.push(k);
63817                 _rules[k] = {
63818                     filter: filter,
63819                     enabled: isEnabled,   // whether the user wants it enabled..
63820                     count: 0,
63821                     currentMax: (max || Infinity),
63822                     defaultMax: (max || Infinity),
63823                     enable: function() { this.enabled = true; this.currentMax = this.defaultMax; },
63824                     disable: function() { this.enabled = false; this.currentMax = 0; },
63825                     hidden: function() {
63826                         return (this.count === 0 && !this.enabled) ||
63827                             this.count > this.currentMax * _cullFactor;
63828                     },
63829                     autoHidden: function() { return this.hidden() && this.currentMax > 0; }
63830                 };
63831             }
63832
63833
63834             defineRule('points', function isPoint(tags, geometry) {
63835                 return geometry === 'point';
63836             }, 200);
63837
63838             defineRule('traffic_roads', function isTrafficRoad(tags) {
63839                 return traffic_roads[tags.highway];
63840             });
63841
63842             defineRule('service_roads', function isServiceRoad(tags) {
63843                 return service_roads[tags.highway];
63844             });
63845
63846             defineRule('paths', function isPath(tags) {
63847                 return paths[tags.highway];
63848             });
63849
63850             defineRule('buildings', function isBuilding(tags) {
63851                 return (
63852                     (!!tags.building && tags.building !== 'no') ||
63853                     tags.parking === 'multi-storey' ||
63854                     tags.parking === 'sheds' ||
63855                     tags.parking === 'carports' ||
63856                     tags.parking === 'garage_boxes'
63857                 );
63858             }, 250);
63859
63860             defineRule('building_parts', function isBuildingPart(tags) {
63861                 return tags['building:part'];
63862             });
63863
63864             defineRule('indoor', function isIndoor(tags) {
63865                 return tags.indoor;
63866             });
63867
63868             defineRule('landuse', function isLanduse(tags, geometry) {
63869                 return geometry === 'area' &&
63870                     !_rules.buildings.filter(tags) &&
63871                     !_rules.building_parts.filter(tags) &&
63872                     !_rules.indoor.filter(tags) &&
63873                     !_rules.water.filter(tags) &&
63874                     !_rules.pistes.filter(tags);
63875             });
63876
63877             defineRule('boundaries', function isBoundary(tags) {
63878                 return (
63879                     !!tags.boundary
63880                 ) && !(
63881                     traffic_roads[tags.highway] ||
63882                     service_roads[tags.highway] ||
63883                     paths[tags.highway] ||
63884                     tags.waterway ||
63885                     tags.railway ||
63886                     tags.landuse ||
63887                     tags.natural ||
63888                     tags.building ||
63889                     tags.power
63890                 );
63891             });
63892
63893             defineRule('water', function isWater(tags) {
63894                 return (
63895                     !!tags.waterway ||
63896                     tags.natural === 'water' ||
63897                     tags.natural === 'coastline' ||
63898                     tags.natural === 'bay' ||
63899                     tags.landuse === 'pond' ||
63900                     tags.landuse === 'basin' ||
63901                     tags.landuse === 'reservoir' ||
63902                     tags.landuse === 'salt_pond'
63903                 );
63904             });
63905
63906             defineRule('rail', function isRail(tags) {
63907                 return (
63908                     !!tags.railway ||
63909                     tags.landuse === 'railway'
63910                 ) && !(
63911                     traffic_roads[tags.highway] ||
63912                     service_roads[tags.highway] ||
63913                     paths[tags.highway]
63914                 );
63915             });
63916
63917             defineRule('pistes', function isPiste(tags) {
63918                 return tags['piste:type'];
63919             });
63920
63921             defineRule('aerialways', function isPiste(tags) {
63922                 return tags.aerialway &&
63923                     tags.aerialway !== 'yes' &&
63924                     tags.aerialway !== 'station';
63925             });
63926
63927             defineRule('power', function isPower(tags) {
63928                 return !!tags.power;
63929             });
63930
63931             // contains a past/future tag, but not in active use as a road/path/cycleway/etc..
63932             defineRule('past_future', function isPastFuture(tags) {
63933                 if (
63934                     traffic_roads[tags.highway] ||
63935                     service_roads[tags.highway] ||
63936                     paths[tags.highway]
63937                 ) { return false; }
63938
63939                 var strings = Object.keys(tags);
63940
63941                 for (var i = 0; i < strings.length; i++) {
63942                     var s = strings[i];
63943                     if (past_futures[s] || past_futures[tags[s]]) { return true; }
63944                 }
63945                 return false;
63946             });
63947
63948             // Lines or areas that don't match another feature filter.
63949             // IMPORTANT: The 'others' feature must be the last one defined,
63950             //   so that code in getMatches can skip this test if `hasMatch = true`
63951             defineRule('others', function isOther(tags, geometry) {
63952                 return (geometry === 'line' || geometry === 'area');
63953             });
63954
63955
63956
63957             features.features = function() {
63958                 return _rules;
63959             };
63960
63961
63962             features.keys = function() {
63963                 return _keys;
63964             };
63965
63966
63967             features.enabled = function(k) {
63968                 if (!arguments.length) {
63969                     return _keys.filter(function(k) { return _rules[k].enabled; });
63970                 }
63971                 return _rules[k] && _rules[k].enabled;
63972             };
63973
63974
63975             features.disabled = function(k) {
63976                 if (!arguments.length) {
63977                     return _keys.filter(function(k) { return !_rules[k].enabled; });
63978                 }
63979                 return _rules[k] && !_rules[k].enabled;
63980             };
63981
63982
63983             features.hidden = function(k) {
63984                 if (!arguments.length) {
63985                     return _keys.filter(function(k) { return _rules[k].hidden(); });
63986                 }
63987                 return _rules[k] && _rules[k].hidden();
63988             };
63989
63990
63991             features.autoHidden = function(k) {
63992                 if (!arguments.length) {
63993                     return _keys.filter(function(k) { return _rules[k].autoHidden(); });
63994                 }
63995                 return _rules[k] && _rules[k].autoHidden();
63996             };
63997
63998
63999             features.enable = function(k) {
64000                 if (_rules[k] && !_rules[k].enabled) {
64001                     _rules[k].enable();
64002                     update();
64003                 }
64004             };
64005
64006             features.enableAll = function() {
64007                 var didEnable = false;
64008                 for (var k in _rules) {
64009                     if (!_rules[k].enabled) {
64010                         didEnable = true;
64011                         _rules[k].enable();
64012                     }
64013                 }
64014                 if (didEnable) { update(); }
64015             };
64016
64017
64018             features.disable = function(k) {
64019                 if (_rules[k] && _rules[k].enabled) {
64020                     _rules[k].disable();
64021                     update();
64022                 }
64023             };
64024
64025             features.disableAll = function() {
64026                 var didDisable = false;
64027                 for (var k in _rules) {
64028                     if (_rules[k].enabled) {
64029                         didDisable = true;
64030                         _rules[k].disable();
64031                     }
64032                 }
64033                 if (didDisable) { update(); }
64034             };
64035
64036
64037             features.toggle = function(k) {
64038                 if (_rules[k]) {
64039                     (function(f) { return f.enabled ? f.disable() : f.enable(); }(_rules[k]));
64040                     update();
64041                 }
64042             };
64043
64044
64045             features.resetStats = function() {
64046                 for (var i = 0; i < _keys.length; i++) {
64047                     _rules[_keys[i]].count = 0;
64048                 }
64049                 dispatch$1.call('change');
64050             };
64051
64052
64053             features.gatherStats = function(d, resolver, dimensions) {
64054                 var needsRedraw = false;
64055                 var types = utilArrayGroupBy(d, 'type');
64056                 var entities = [].concat(types.relation || [], types.way || [], types.node || []);
64057                 var currHidden, geometry, matches, i, j;
64058
64059                 for (i = 0; i < _keys.length; i++) {
64060                     _rules[_keys[i]].count = 0;
64061                 }
64062
64063                 // adjust the threshold for point/building culling based on viewport size..
64064                 // a _cullFactor of 1 corresponds to a 1000x1000px viewport..
64065                 _cullFactor = dimensions[0] * dimensions[1] / 1000000;
64066
64067                 for (i = 0; i < entities.length; i++) {
64068                     geometry = entities[i].geometry(resolver);
64069                     matches = Object.keys(features.getMatches(entities[i], resolver, geometry));
64070                     for (j = 0; j < matches.length; j++) {
64071                         _rules[matches[j]].count++;
64072                     }
64073                 }
64074
64075                 currHidden = features.hidden();
64076                 if (currHidden !== _hidden) {
64077                     _hidden = currHidden;
64078                     needsRedraw = true;
64079                     dispatch$1.call('change');
64080                 }
64081
64082                 return needsRedraw;
64083             };
64084
64085
64086             features.stats = function() {
64087                 for (var i = 0; i < _keys.length; i++) {
64088                     _stats[_keys[i]] = _rules[_keys[i]].count;
64089                 }
64090
64091                 return _stats;
64092             };
64093
64094
64095             features.clear = function(d) {
64096                 for (var i = 0; i < d.length; i++) {
64097                     features.clearEntity(d[i]);
64098                 }
64099             };
64100
64101
64102             features.clearEntity = function(entity) {
64103                 delete _cache[osmEntity.key(entity)];
64104             };
64105
64106
64107             features.reset = function() {
64108                 Array.from(_deferred).forEach(function(handle) {
64109                     window.cancelIdleCallback(handle);
64110                     _deferred.delete(handle);
64111                 });
64112
64113                 _cache = {};
64114             };
64115
64116             // only certain relations are worth checking
64117             function relationShouldBeChecked(relation) {
64118                 // multipolygon features have `area` geometry and aren't checked here
64119                 return relation.tags.type === 'boundary';
64120             }
64121
64122             features.getMatches = function(entity, resolver, geometry) {
64123                 if (geometry === 'vertex' ||
64124                     (geometry === 'relation' && !relationShouldBeChecked(entity))) { return {}; }
64125
64126                 var ent = osmEntity.key(entity);
64127                 if (!_cache[ent]) {
64128                     _cache[ent] = {};
64129                 }
64130
64131                 if (!_cache[ent].matches) {
64132                     var matches = {};
64133                     var hasMatch = false;
64134
64135                     for (var i = 0; i < _keys.length; i++) {
64136                         if (_keys[i] === 'others') {
64137                             if (hasMatch) { continue; }
64138
64139                             // If an entity...
64140                             //   1. is a way that hasn't matched other 'interesting' feature rules,
64141                             if (entity.type === 'way') {
64142                                 var parents = features.getParents(entity, resolver, geometry);
64143
64144                                 //   2a. belongs only to a single multipolygon relation
64145                                 if ((parents.length === 1 && parents[0].isMultipolygon()) ||
64146                                     // 2b. or belongs only to boundary relations
64147                                     (parents.length > 0 && parents.every(function(parent) { return parent.tags.type === 'boundary'; }))) {
64148
64149                                     // ...then match whatever feature rules the parent relation has matched.
64150                                     // see #2548, #2887
64151                                     //
64152                                     // IMPORTANT:
64153                                     // For this to work, getMatches must be called on relations before ways.
64154                                     //
64155                                     var pkey = osmEntity.key(parents[0]);
64156                                     if (_cache[pkey] && _cache[pkey].matches) {
64157                                         matches = Object.assign({}, _cache[pkey].matches);  // shallow copy
64158                                         continue;
64159                                     }
64160                                 }
64161                             }
64162                         }
64163
64164                         if (_rules[_keys[i]].filter(entity.tags, geometry)) {
64165                             matches[_keys[i]] = hasMatch = true;
64166                         }
64167                     }
64168                     _cache[ent].matches = matches;
64169                 }
64170
64171                 return _cache[ent].matches;
64172             };
64173
64174
64175             features.getParents = function(entity, resolver, geometry) {
64176                 if (geometry === 'point') { return []; }
64177
64178                 var ent = osmEntity.key(entity);
64179                 if (!_cache[ent]) {
64180                     _cache[ent] = {};
64181                 }
64182
64183                 if (!_cache[ent].parents) {
64184                     var parents = [];
64185                     if (geometry === 'vertex') {
64186                         parents = resolver.parentWays(entity);
64187                     } else {   // 'line', 'area', 'relation'
64188                         parents = resolver.parentRelations(entity);
64189                     }
64190                     _cache[ent].parents = parents;
64191                 }
64192                 return _cache[ent].parents;
64193             };
64194
64195
64196             features.isHiddenPreset = function(preset, geometry) {
64197                 if (!_hidden.length) { return false; }
64198                 if (!preset.tags) { return false; }
64199
64200                 var test = preset.setTags({}, geometry);
64201                 for (var key in _rules) {
64202                     if (_rules[key].filter(test, geometry)) {
64203                         if (_hidden.indexOf(key) !== -1) {
64204                             return key;
64205                         }
64206                         return false;
64207                     }
64208                 }
64209                 return false;
64210             };
64211
64212
64213             features.isHiddenFeature = function(entity, resolver, geometry) {
64214                 if (!_hidden.length) { return false; }
64215                 if (!entity.version) { return false; }
64216                 if (_forceVisible[entity.id]) { return false; }
64217
64218                 var matches = Object.keys(features.getMatches(entity, resolver, geometry));
64219                 return matches.length && matches.every(function(k) { return features.hidden(k); });
64220             };
64221
64222
64223             features.isHiddenChild = function(entity, resolver, geometry) {
64224                 if (!_hidden.length) { return false; }
64225                 if (!entity.version || geometry === 'point') { return false; }
64226                 if (_forceVisible[entity.id]) { return false; }
64227
64228                 var parents = features.getParents(entity, resolver, geometry);
64229                 if (!parents.length) { return false; }
64230
64231                 for (var i = 0; i < parents.length; i++) {
64232                     if (!features.isHidden(parents[i], resolver, parents[i].geometry(resolver))) {
64233                         return false;
64234                     }
64235                 }
64236                 return true;
64237             };
64238
64239
64240             features.hasHiddenConnections = function(entity, resolver) {
64241                 if (!_hidden.length) { return false; }
64242
64243                 var childNodes, connections;
64244                 if (entity.type === 'midpoint') {
64245                     childNodes = [resolver.entity(entity.edge[0]), resolver.entity(entity.edge[1])];
64246                     connections = [];
64247                 } else {
64248                     childNodes = entity.nodes ? resolver.childNodes(entity) : [];
64249                     connections = features.getParents(entity, resolver, entity.geometry(resolver));
64250                 }
64251
64252                 // gather ways connected to child nodes..
64253                 connections = childNodes.reduce(function(result, e) {
64254                     return resolver.isShared(e) ? utilArrayUnion(result, resolver.parentWays(e)) : result;
64255                 }, connections);
64256
64257                 return connections.some(function(e) {
64258                     return features.isHidden(e, resolver, e.geometry(resolver));
64259                 });
64260             };
64261
64262
64263             features.isHidden = function(entity, resolver, geometry) {
64264                 if (!_hidden.length) { return false; }
64265                 if (!entity.version) { return false; }
64266
64267                 var fn = (geometry === 'vertex' ? features.isHiddenChild : features.isHiddenFeature);
64268                 return fn(entity, resolver, geometry);
64269             };
64270
64271
64272             features.filter = function(d, resolver) {
64273                 if (!_hidden.length) { return d; }
64274
64275                 var result = [];
64276                 for (var i = 0; i < d.length; i++) {
64277                     var entity = d[i];
64278                     if (!features.isHidden(entity, resolver, entity.geometry(resolver))) {
64279                         result.push(entity);
64280                     }
64281                 }
64282                 return result;
64283             };
64284
64285
64286             features.forceVisible = function(entityIDs) {
64287                 if (!arguments.length) { return Object.keys(_forceVisible); }
64288
64289                 _forceVisible = {};
64290                 for (var i = 0; i < entityIDs.length; i++) {
64291                     _forceVisible[entityIDs[i]] = true;
64292                     var entity = context.hasEntity(entityIDs[i]);
64293                     if (entity && entity.type === 'relation') {
64294                         // also show relation members (one level deep)
64295                         for (var j in entity.members) {
64296                             _forceVisible[entity.members[j].id] = true;
64297                         }
64298                     }
64299                 }
64300                 return features;
64301             };
64302
64303
64304             features.init = function() {
64305                 var storage = corePreferences('disabled-features');
64306                 if (storage) {
64307                     var storageDisabled = storage.replace(/;/g, ',').split(',');
64308                     storageDisabled.forEach(features.disable);
64309                 }
64310
64311                 var hash = utilStringQs(window.location.hash);
64312                 if (hash.disable_features) {
64313                     var hashDisabled = hash.disable_features.replace(/;/g, ',').split(',');
64314                     hashDisabled.forEach(features.disable);
64315                 }
64316             };
64317
64318
64319             // warm up the feature matching cache upon merging fetched data
64320             context.history().on('merge.features', function(newEntities) {
64321                 if (!newEntities) { return; }
64322                 var handle = window.requestIdleCallback(function() {
64323                     var graph = context.graph();
64324                     var types = utilArrayGroupBy(newEntities, 'type');
64325                     // ensure that getMatches is called on relations before ways
64326                     var entities = [].concat(types.relation || [], types.way || [], types.node || []);
64327                     for (var i = 0; i < entities.length; i++) {
64328                         var geometry = entities[i].geometry(graph);
64329                         features.getMatches(entities[i], graph, geometry);
64330                     }
64331                 });
64332                 _deferred.add(handle);
64333             });
64334
64335
64336             return features;
64337         }
64338
64339         // Touch targets control which other vertices we can drag a vertex onto.
64340         //
64341         // - the activeID - nope
64342         // - 1 away (adjacent) to the activeID - yes (vertices will be merged)
64343         // - 2 away from the activeID - nope (would create a self intersecting segment)
64344         // - all others on a linear way - yes
64345         // - all others on a closed way - nope (would create a self intersecting polygon)
64346         //
64347         // returns
64348         // 0 = active vertex - no touch/connect
64349         // 1 = passive vertex - yes touch/connect
64350         // 2 = adjacent vertex - yes but pay attention segmenting a line here
64351         //
64352         function svgPassiveVertex(node, graph, activeID) {
64353             if (!activeID) { return 1; }
64354             if (activeID === node.id) { return 0; }
64355
64356             var parents = graph.parentWays(node);
64357
64358             var i, j, nodes, isClosed, ix1, ix2, ix3, ix4, max;
64359
64360             for (i = 0; i < parents.length; i++) {
64361                 nodes = parents[i].nodes;
64362                 isClosed = parents[i].isClosed();
64363                 for (j = 0; j < nodes.length; j++) {   // find this vertex, look nearby
64364                     if (nodes[j] === node.id) {
64365                         ix1 = j - 2;
64366                         ix2 = j - 1;
64367                         ix3 = j + 1;
64368                         ix4 = j + 2;
64369
64370                         if (isClosed) {  // wraparound if needed
64371                             max = nodes.length - 1;
64372                             if (ix1 < 0)   { ix1 = max + ix1; }
64373                             if (ix2 < 0)   { ix2 = max + ix2; }
64374                             if (ix3 > max) { ix3 = ix3 - max; }
64375                             if (ix4 > max) { ix4 = ix4 - max; }
64376                         }
64377
64378                         if (nodes[ix1] === activeID) { return 0; }        // no - prevent self intersect
64379                         else if (nodes[ix2] === activeID) { return 2; }   // ok - adjacent
64380                         else if (nodes[ix3] === activeID) { return 2; }   // ok - adjacent
64381                         else if (nodes[ix4] === activeID) { return 0; }   // no - prevent self intersect
64382                         else if (isClosed && nodes.indexOf(activeID) !== -1) { return 0; }  // no - prevent self intersect
64383                     }
64384                 }
64385             }
64386
64387             return 1;   // ok
64388         }
64389
64390
64391         function svgMarkerSegments(projection, graph, dt,
64392                                           shouldReverse,
64393                                           bothDirections) {
64394             return function(entity) {
64395                 var i = 0;
64396                 var offset = dt;
64397                 var segments = [];
64398                 var clip = d3_geoIdentity().clipExtent(projection.clipExtent()).stream;
64399                 var coordinates = graph.childNodes(entity).map(function(n) { return n.loc; });
64400                 var a, b;
64401
64402                 if (shouldReverse(entity)) {
64403                     coordinates.reverse();
64404                 }
64405
64406                 d3_geoStream({
64407                     type: 'LineString',
64408                     coordinates: coordinates
64409                 }, projection.stream(clip({
64410                     lineStart: function() {},
64411                     lineEnd: function() { a = null; },
64412                     point: function(x, y) {
64413                         b = [x, y];
64414
64415                         if (a) {
64416                             var span = geoVecLength(a, b) - offset;
64417
64418                             if (span >= 0) {
64419                                 var heading = geoVecAngle(a, b);
64420                                 var dx = dt * Math.cos(heading);
64421                                 var dy = dt * Math.sin(heading);
64422                                 var p = [
64423                                     a[0] + offset * Math.cos(heading),
64424                                     a[1] + offset * Math.sin(heading)
64425                                 ];
64426
64427                                 // gather coordinates
64428                                 var coord = [a, p];
64429                                 for (span -= dt; span >= 0; span -= dt) {
64430                                     p = geoVecAdd(p, [dx, dy]);
64431                                     coord.push(p);
64432                                 }
64433                                 coord.push(b);
64434
64435                                 // generate svg paths
64436                                 var segment = '';
64437                                 var j;
64438
64439                                 for (j = 0; j < coord.length; j++) {
64440                                     segment += (j === 0 ? 'M' : 'L') + coord[j][0] + ',' + coord[j][1];
64441                                 }
64442                                 segments.push({ id: entity.id, index: i++, d: segment });
64443
64444                                 if (bothDirections(entity)) {
64445                                     segment = '';
64446                                     for (j = coord.length - 1; j >= 0; j--) {
64447                                         segment += (j === coord.length - 1 ? 'M' : 'L') + coord[j][0] + ',' + coord[j][1];
64448                                     }
64449                                     segments.push({ id: entity.id, index: i++, d: segment });
64450                                 }
64451                             }
64452
64453                             offset = -span;
64454                         }
64455
64456                         a = b;
64457                     }
64458                 })));
64459
64460                 return segments;
64461             };
64462         }
64463
64464
64465         function svgPath(projection, graph, isArea) {
64466
64467             // Explanation of magic numbers:
64468             // "padding" here allows space for strokes to extend beyond the viewport,
64469             // so that the stroke isn't drawn along the edge of the viewport when
64470             // the shape is clipped.
64471             //
64472             // When drawing lines, pad viewport by 5px.
64473             // When drawing areas, pad viewport by 65px in each direction to allow
64474             // for 60px area fill stroke (see ".fill-partial path.fill" css rule)
64475
64476             var cache = {};
64477             var padding = isArea ? 65 : 5;
64478             var viewport = projection.clipExtent();
64479             var paddedExtent = [
64480                 [viewport[0][0] - padding, viewport[0][1] - padding],
64481                 [viewport[1][0] + padding, viewport[1][1] + padding]
64482             ];
64483             var clip = d3_geoIdentity().clipExtent(paddedExtent).stream;
64484             var project = projection.stream;
64485             var path = d3_geoPath()
64486                 .projection({stream: function(output) { return project(clip(output)); }});
64487
64488             var svgpath = function(entity) {
64489                 if (entity.id in cache) {
64490                     return cache[entity.id];
64491                 } else {
64492                     return cache[entity.id] = path(entity.asGeoJSON(graph));
64493                 }
64494             };
64495
64496             svgpath.geojson = function(d) {
64497                 if (d.__featurehash__ !== undefined) {
64498                     if (d.__featurehash__ in cache) {
64499                         return cache[d.__featurehash__];
64500                     } else {
64501                         return cache[d.__featurehash__] = path(d);
64502                     }
64503                 } else {
64504                     return path(d);
64505                 }
64506             };
64507
64508             return svgpath;
64509         }
64510
64511
64512         function svgPointTransform(projection) {
64513             var svgpoint = function(entity) {
64514                 // http://jsperf.com/short-array-join
64515                 var pt = projection(entity.loc);
64516                 return 'translate(' + pt[0] + ',' + pt[1] + ')';
64517             };
64518
64519             svgpoint.geojson = function(d) {
64520                 return svgpoint(d.properties.entity);
64521             };
64522
64523             return svgpoint;
64524         }
64525
64526
64527         function svgRelationMemberTags(graph) {
64528             return function(entity) {
64529                 var tags = entity.tags;
64530                 var shouldCopyMultipolygonTags = !entity.hasInterestingTags();
64531                 graph.parentRelations(entity).forEach(function(relation) {
64532                     var type = relation.tags.type;
64533                     if ((type === 'multipolygon' && shouldCopyMultipolygonTags) || type === 'boundary') {
64534                         tags = Object.assign({}, relation.tags, tags);
64535                     }
64536                 });
64537                 return tags;
64538             };
64539         }
64540
64541
64542         function svgSegmentWay(way, graph, activeID) {
64543             // When there is no activeID, we can memoize this expensive computation
64544             if (activeID === undefined) {
64545                 return graph.transient(way, 'waySegments', getWaySegments);
64546             } else {
64547                 return getWaySegments();
64548             }
64549
64550             function getWaySegments() {
64551                 var isActiveWay = (way.nodes.indexOf(activeID) !== -1);
64552                 var features = { passive: [], active: [] };
64553                 var start = {};
64554                 var end = {};
64555                 var node, type;
64556
64557                 for (var i = 0; i < way.nodes.length; i++) {
64558                     node = graph.entity(way.nodes[i]);
64559                     type = svgPassiveVertex(node, graph, activeID);
64560                     end = { node: node, type: type };
64561
64562                     if (start.type !== undefined) {
64563                         if (start.node.id === activeID || end.node.id === activeID) ; else if (isActiveWay && (start.type === 2 || end.type === 2)) {   // one adjacent vertex
64564                             pushActive(start, end, i);
64565                         } else if (start.type === 0 && end.type === 0) {   // both active vertices
64566                             pushActive(start, end, i);
64567                         } else {
64568                             pushPassive(start, end, i);
64569                         }
64570                     }
64571
64572                     start = end;
64573                 }
64574
64575                 return features;
64576
64577                 function pushActive(start, end, index) {
64578                     features.active.push({
64579                         type: 'Feature',
64580                         id: way.id + '-' + index + '-nope',
64581                         properties: {
64582                             nope: true,
64583                             target: true,
64584                             entity: way,
64585                             nodes: [start.node, end.node],
64586                             index: index
64587                         },
64588                         geometry: {
64589                             type: 'LineString',
64590                             coordinates: [start.node.loc, end.node.loc]
64591                         }
64592                     });
64593                 }
64594
64595                 function pushPassive(start, end, index) {
64596                     features.passive.push({
64597                         type: 'Feature',
64598                         id: way.id + '-' + index,
64599                         properties: {
64600                             target: true,
64601                             entity: way,
64602                             nodes: [start.node, end.node],
64603                             index: index
64604                         },
64605                         geometry: {
64606                             type: 'LineString',
64607                             coordinates: [start.node.loc, end.node.loc]
64608                         }
64609                     });
64610                 }
64611             }
64612         }
64613
64614         function svgTagClasses() {
64615             var primaries = [
64616                 'building', 'highway', 'railway', 'waterway', 'aeroway', 'aerialway',
64617                 'piste:type', 'boundary', 'power', 'amenity', 'natural', 'landuse',
64618                 'leisure', 'military', 'place', 'man_made', 'route', 'attraction',
64619                 'building:part', 'indoor'
64620             ];
64621             var statuses = [
64622                 // nonexistent, might be built
64623                 'proposed', 'planned',
64624                 // under maintentance or between groundbreaking and opening
64625                 'construction',
64626                 // existent but not functional
64627                 'disused',
64628                 // dilapidated to nonexistent
64629                 'abandoned',
64630                 // nonexistent, still may appear in imagery
64631                 'dismantled', 'razed', 'demolished', 'obliterated',
64632                 // existent occasionally, e.g. stormwater drainage basin
64633                 'intermittent'
64634             ];
64635             var secondaries = [
64636                 'oneway', 'bridge', 'tunnel', 'embankment', 'cutting', 'barrier',
64637                 'surface', 'tracktype', 'footway', 'crossing', 'service', 'sport',
64638                 'public_transport', 'location', 'parking', 'golf', 'type', 'leisure',
64639                 'man_made', 'indoor'
64640             ];
64641             var _tags = function(entity) { return entity.tags; };
64642
64643
64644             var tagClasses = function(selection) {
64645                 selection.each(function tagClassesEach(entity) {
64646                     var value = this.className;
64647
64648                     if (value.baseVal !== undefined) {
64649                         value = value.baseVal;
64650                     }
64651
64652                     var t = _tags(entity);
64653
64654                     var computed = tagClasses.getClassesString(t, value);
64655
64656                     if (computed !== value) {
64657                         select(this).attr('class', computed);
64658                     }
64659                 });
64660             };
64661
64662
64663             tagClasses.getClassesString = function(t, value) {
64664                 var primary, status;
64665                 var i, j, k, v;
64666
64667                 // in some situations we want to render perimeter strokes a certain way
64668                 var overrideGeometry;
64669                 if (/\bstroke\b/.test(value)) {
64670                     if (!!t.barrier && t.barrier !== 'no') {
64671                         overrideGeometry = 'line';
64672                     }
64673                 }
64674
64675                 // preserve base classes (nothing with `tag-`)
64676                 var classes = value.trim().split(/\s+/)
64677                     .filter(function(klass) {
64678                         return klass.length && !/^tag-/.test(klass);
64679                     })
64680                     .map(function(klass) {  // special overrides for some perimeter strokes
64681                         return (klass === 'line' || klass === 'area') ? (overrideGeometry || klass) : klass;
64682                     });
64683
64684                 // pick at most one primary classification tag..
64685                 for (i = 0; i < primaries.length; i++) {
64686                     k = primaries[i];
64687                     v = t[k];
64688                     if (!v || v === 'no') { continue; }
64689
64690                     if (k === 'piste:type') {  // avoid a ':' in the class name
64691                         k = 'piste';
64692                     } else if (k === 'building:part') {  // avoid a ':' in the class name
64693                         k = 'building_part';
64694                     }
64695
64696                     primary = k;
64697                     if (statuses.indexOf(v) !== -1) {   // e.g. `railway=abandoned`
64698                         status = v;
64699                         classes.push('tag-' + k);
64700                     } else {
64701                         classes.push('tag-' + k);
64702                         classes.push('tag-' + k + '-' + v);
64703                     }
64704
64705                     break;
64706                 }
64707
64708                 if (!primary) {
64709                     for (i = 0; i < statuses.length; i++) {
64710                         for (j = 0; j < primaries.length; j++) {
64711                             k = statuses[i] + ':' + primaries[j];  // e.g. `demolished:building=yes`
64712                             v = t[k];
64713                             if (!v || v === 'no') { continue; }
64714
64715                             status = statuses[i];
64716                             break;
64717                         }
64718                     }
64719                 }
64720
64721                 // add at most one status tag, only if relates to primary tag..
64722                 if (!status) {
64723                     for (i = 0; i < statuses.length; i++) {
64724                         k = statuses[i];
64725                         v = t[k];
64726                         if (!v || v === 'no') { continue; }
64727
64728                         if (v === 'yes') {   // e.g. `railway=rail + abandoned=yes`
64729                             status = k;
64730                         }
64731                         else if (primary && primary === v) {  // e.g. `railway=rail + abandoned=railway`
64732                             status = k;
64733                         } else if (!primary && primaries.indexOf(v) !== -1) {  // e.g. `abandoned=railway`
64734                             status = k;
64735                             primary = v;
64736                             classes.push('tag-' + v);
64737                         }  // else ignore e.g.  `highway=path + abandoned=railway`
64738
64739                         if (status) { break; }
64740                     }
64741                 }
64742
64743                 if (status) {
64744                     classes.push('tag-status');
64745                     classes.push('tag-status-' + status);
64746                 }
64747
64748                 // add any secondary tags
64749                 for (i = 0; i < secondaries.length; i++) {
64750                     k = secondaries[i];
64751                     v = t[k];
64752                     if (!v || v === 'no' || k === primary) { continue; }
64753                     classes.push('tag-' + k);
64754                     classes.push('tag-' + k + '-' + v);
64755                 }
64756
64757                 // For highways, look for surface tagging..
64758                 if ((primary === 'highway' && !osmPathHighwayTagValues[t.highway]) || primary === 'aeroway') {
64759                     var surface = t.highway === 'track' ? 'unpaved' : 'paved';
64760                     for (k in t) {
64761                         v = t[k];
64762                         if (k in osmPavedTags) {
64763                             surface = osmPavedTags[k][v] ? 'paved' : 'unpaved';
64764                         }
64765                         if (k in osmSemipavedTags && !!osmSemipavedTags[k][v]) {
64766                             surface = 'semipaved';
64767                         }
64768                     }
64769                     classes.push('tag-' + surface);
64770                 }
64771
64772                 // If this is a wikidata-tagged item, add a class for that..
64773                 if (t.wikidata || t['brand:wikidata']) {
64774                     classes.push('tag-wikidata');
64775                 }
64776
64777                 return classes.join(' ').trim();
64778             };
64779
64780
64781             tagClasses.tags = function(val) {
64782                 if (!arguments.length) { return _tags; }
64783                 _tags = val;
64784                 return tagClasses;
64785             };
64786
64787             return tagClasses;
64788         }
64789
64790         // Patterns only work in Firefox when set directly on element.
64791         // (This is not a bug: https://bugzilla.mozilla.org/show_bug.cgi?id=750632)
64792         var patterns = {
64793             // tag - pattern name
64794             // -or-
64795             // tag - value - pattern name
64796             // -or-
64797             // tag - value - rules (optional tag-values, pattern name)
64798             // (matches earlier rules first, so fallback should be last entry)
64799             amenity: {
64800                 grave_yard: 'cemetery',
64801                 fountain: 'water_standing'
64802             },
64803             landuse: {
64804                 cemetery: [
64805                     { religion: 'christian', pattern: 'cemetery_christian' },
64806                     { religion: 'buddhist', pattern: 'cemetery_buddhist' },
64807                     { religion: 'muslim', pattern: 'cemetery_muslim' },
64808                     { religion: 'jewish', pattern: 'cemetery_jewish' },
64809                     { pattern: 'cemetery' }
64810                 ],
64811                 construction: 'construction',
64812                 farmland: 'farmland',
64813                 farmyard: 'farmyard',
64814                 forest: [
64815                     { leaf_type: 'broadleaved', pattern: 'forest_broadleaved' },
64816                     { leaf_type: 'needleleaved', pattern: 'forest_needleleaved' },
64817                     { leaf_type: 'leafless', pattern: 'forest_leafless' },
64818                     { pattern: 'forest' } // same as 'leaf_type:mixed'
64819                 ],
64820                 grave_yard: 'cemetery',
64821                 grass: [
64822                     { golf: 'green', pattern: 'golf_green' },
64823                     { pattern: 'grass' } ],
64824                 landfill: 'landfill',
64825                 meadow: 'meadow',
64826                 military: 'construction',
64827                 orchard: 'orchard',
64828                 quarry: 'quarry',
64829                 vineyard: 'vineyard'
64830             },
64831             natural: {
64832                 beach: 'beach',
64833                 grassland: 'grass',
64834                 sand: 'beach',
64835                 scrub: 'scrub',
64836                 water: [
64837                     { water: 'pond', pattern: 'pond' },
64838                     { water: 'reservoir', pattern: 'water_standing' },
64839                     { pattern: 'waves' }
64840                 ],
64841                 wetland: [
64842                     { wetland: 'marsh', pattern: 'wetland_marsh' },
64843                     { wetland: 'swamp', pattern: 'wetland_swamp' },
64844                     { wetland: 'bog', pattern: 'wetland_bog' },
64845                     { wetland: 'reedbed', pattern: 'wetland_reedbed' },
64846                     { pattern: 'wetland' }
64847                 ],
64848                 wood: [
64849                     { leaf_type: 'broadleaved', pattern: 'forest_broadleaved' },
64850                     { leaf_type: 'needleleaved', pattern: 'forest_needleleaved' },
64851                     { leaf_type: 'leafless', pattern: 'forest_leafless' },
64852                     { pattern: 'forest' } // same as 'leaf_type:mixed'
64853                 ]
64854             },
64855             traffic_calming: {
64856                 island: [
64857                     { surface: 'grass', pattern: 'grass' } ],
64858                 chicane: [
64859                     { surface: 'grass', pattern: 'grass' } ],
64860                 choker: [
64861                     { surface: 'grass', pattern: 'grass' } ]
64862             }
64863         };
64864
64865         function svgTagPattern(tags) {
64866             // Skip pattern filling if this is a building (buildings don't get patterns applied)
64867             if (tags.building && tags.building !== 'no') {
64868                 return null;
64869             }
64870
64871             for (var tag in patterns) {
64872                 var entityValue = tags[tag];
64873                 if (!entityValue) { continue; }
64874
64875                 if (typeof patterns[tag] === 'string') { // extra short syntax (just tag) - pattern name
64876                     return 'pattern-' + patterns[tag];
64877                 } else {
64878                     var values = patterns[tag];
64879                     for (var value in values) {
64880                         if (entityValue !== value) { continue; }
64881
64882                         var rules = values[value];
64883                         if (typeof rules === 'string') { // short syntax - pattern name
64884                             return 'pattern-' + rules;
64885                         }
64886
64887                         // long syntax - rule array
64888                         for (var ruleKey in rules) {
64889                             var rule = rules[ruleKey];
64890
64891                             var pass = true;
64892                             for (var criterion in rule) {
64893                                 if (criterion !== 'pattern') { // reserved for pattern name
64894                                     // The only rule is a required tag-value pair
64895                                     var v = tags[criterion];
64896                                     if (!v || v !== rule[criterion]) {
64897                                         pass = false;
64898                                         break;
64899                                     }
64900                                 }
64901                             }
64902
64903                             if (pass) {
64904                                 return 'pattern-' + rule.pattern;
64905                             }
64906                         }
64907                     }
64908                 }
64909             }
64910
64911             return null;
64912         }
64913
64914         function svgAreas(projection, context) {
64915
64916
64917             function getPatternStyle(tags) {
64918                 var imageID = svgTagPattern(tags);
64919                 if (imageID) {
64920                     return 'url("#ideditor-' + imageID + '")';
64921                 }
64922                 return '';
64923             }
64924
64925
64926             function drawTargets(selection, graph, entities, filter) {
64927                 var targetClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
64928                 var nopeClass = context.getDebug('target') ? 'red ' : 'nocolor ';
64929                 var getPath = svgPath(projection).geojson;
64930                 var activeID = context.activeID();
64931                 var base = context.history().base();
64932
64933                 // The targets and nopes will be MultiLineString sub-segments of the ways
64934                 var data = { targets: [], nopes: [] };
64935
64936                 entities.forEach(function(way) {
64937                     var features = svgSegmentWay(way, graph, activeID);
64938                     data.targets.push.apply(data.targets, features.passive);
64939                     data.nopes.push.apply(data.nopes, features.active);
64940                 });
64941
64942
64943                 // Targets allow hover and vertex snapping
64944                 var targetData = data.targets.filter(getPath);
64945                 var targets = selection.selectAll('.area.target-allowed')
64946                     .filter(function(d) { return filter(d.properties.entity); })
64947                     .data(targetData, function key(d) { return d.id; });
64948
64949                 // exit
64950                 targets.exit()
64951                     .remove();
64952
64953                 var segmentWasEdited = function(d) {
64954                     var wayID = d.properties.entity.id;
64955                     // if the whole line was edited, don't draw segment changes
64956                     if (!base.entities[wayID] ||
64957                         !fastDeepEqual(graph.entities[wayID].nodes, base.entities[wayID].nodes)) {
64958                         return false;
64959                     }
64960                     return d.properties.nodes.some(function(n) {
64961                         return !base.entities[n.id] ||
64962                                !fastDeepEqual(graph.entities[n.id].loc, base.entities[n.id].loc);
64963                     });
64964                 };
64965
64966                 // enter/update
64967                 targets.enter()
64968                     .append('path')
64969                     .merge(targets)
64970                     .attr('d', getPath)
64971                     .attr('class', function(d) { return 'way area target target-allowed ' + targetClass + d.id; })
64972                     .classed('segment-edited', segmentWasEdited);
64973
64974
64975                 // NOPE
64976                 var nopeData = data.nopes.filter(getPath);
64977                 var nopes = selection.selectAll('.area.target-nope')
64978                     .filter(function(d) { return filter(d.properties.entity); })
64979                     .data(nopeData, function key(d) { return d.id; });
64980
64981                 // exit
64982                 nopes.exit()
64983                     .remove();
64984
64985                 // enter/update
64986                 nopes.enter()
64987                     .append('path')
64988                     .merge(nopes)
64989                     .attr('d', getPath)
64990                     .attr('class', function(d) { return 'way area target target-nope ' + nopeClass + d.id; })
64991                     .classed('segment-edited', segmentWasEdited);
64992             }
64993
64994
64995             function drawAreas(selection, graph, entities, filter) {
64996                 var path = svgPath(projection, graph, true);
64997                 var areas = {};
64998                 var multipolygon;
64999                 var base = context.history().base();
65000
65001                 for (var i = 0; i < entities.length; i++) {
65002                     var entity = entities[i];
65003                     if (entity.geometry(graph) !== 'area') { continue; }
65004
65005                     multipolygon = osmIsOldMultipolygonOuterMember(entity, graph);
65006                     if (multipolygon) {
65007                         areas[multipolygon.id] = {
65008                             entity: multipolygon.mergeTags(entity.tags),
65009                             area: Math.abs(entity.area(graph))
65010                         };
65011                     } else if (!areas[entity.id]) {
65012                         areas[entity.id] = {
65013                             entity: entity,
65014                             area: Math.abs(entity.area(graph))
65015                         };
65016                     }
65017                 }
65018
65019                 var fills = Object.values(areas).filter(function hasPath(a) { return path(a.entity); });
65020                 fills.sort(function areaSort(a, b) { return b.area - a.area; });
65021                 fills = fills.map(function(a) { return a.entity; });
65022
65023                 var strokes = fills.filter(function(area) { return area.type === 'way'; });
65024
65025                 var data = {
65026                     clip: fills,
65027                     shadow: strokes,
65028                     stroke: strokes,
65029                     fill: fills
65030                 };
65031
65032                 var clipPaths = context.surface().selectAll('defs').selectAll('.clipPath-osm')
65033                    .filter(filter)
65034                    .data(data.clip, osmEntity.key);
65035
65036                 clipPaths.exit()
65037                    .remove();
65038
65039                 var clipPathsEnter = clipPaths.enter()
65040                    .append('clipPath')
65041                    .attr('class', 'clipPath-osm')
65042                    .attr('id', function(entity) { return 'ideditor-' + entity.id + '-clippath'; });
65043
65044                 clipPathsEnter
65045                    .append('path');
65046
65047                 clipPaths.merge(clipPathsEnter)
65048                    .selectAll('path')
65049                    .attr('d', path);
65050
65051
65052                 var drawLayer = selection.selectAll('.layer-osm.areas');
65053                 var touchLayer = selection.selectAll('.layer-touch.areas');
65054
65055                 // Draw areas..
65056                 var areagroup = drawLayer
65057                     .selectAll('g.areagroup')
65058                     .data(['fill', 'shadow', 'stroke']);
65059
65060                 areagroup = areagroup.enter()
65061                     .append('g')
65062                     .attr('class', function(d) { return 'areagroup area-' + d; })
65063                     .merge(areagroup);
65064
65065                 var paths = areagroup
65066                     .selectAll('path')
65067                     .filter(filter)
65068                     .data(function(layer) { return data[layer]; }, osmEntity.key);
65069
65070                 paths.exit()
65071                     .remove();
65072
65073
65074                 var fillpaths = selection.selectAll('.area-fill path.area').nodes();
65075                 var bisect = d3_bisector(function(node) { return -node.__data__.area(graph); }).left;
65076
65077                 function sortedByArea(entity) {
65078                     if (this._parent.__data__ === 'fill') {
65079                         return fillpaths[bisect(fillpaths, -entity.area(graph))];
65080                     }
65081                 }
65082
65083                 paths = paths.enter()
65084                     .insert('path', sortedByArea)
65085                     .merge(paths)
65086                     .each(function(entity) {
65087                         var layer = this.parentNode.__data__;
65088                         this.setAttribute('class', entity.type + ' area ' + layer + ' ' + entity.id);
65089
65090                         if (layer === 'fill') {
65091                             this.setAttribute('clip-path', 'url(#ideditor-' + entity.id + '-clippath)');
65092                             this.style.fill = this.style.stroke = getPatternStyle(entity.tags);
65093                         }
65094                     })
65095                     .classed('added', function(d) {
65096                         return !base.entities[d.id];
65097                     })
65098                     .classed('geometry-edited', function(d) {
65099                         return graph.entities[d.id] &&
65100                             base.entities[d.id] &&
65101                             !fastDeepEqual(graph.entities[d.id].nodes, base.entities[d.id].nodes);
65102                     })
65103                     .classed('retagged', function(d) {
65104                         return graph.entities[d.id] &&
65105                             base.entities[d.id] &&
65106                             !fastDeepEqual(graph.entities[d.id].tags, base.entities[d.id].tags);
65107                     })
65108                     .call(svgTagClasses())
65109                     .attr('d', path);
65110
65111
65112                 // Draw touch targets..
65113                 touchLayer
65114                     .call(drawTargets, graph, data.stroke, filter);
65115             }
65116
65117             return drawAreas;
65118         }
65119
65120         //[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]
65121         //[4a]          NameChar           ::=          NameStartChar | "-" | "." | [0-9] | #xB7 | [#x0300-#x036F] | [#x203F-#x2040]
65122         //[5]           Name       ::=          NameStartChar (NameChar)*
65123         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
65124         var nameChar = new RegExp("[\\-\\.0-9"+nameStartChar.source.slice(1,-1)+"\\u00B7\\u0300-\\u036F\\u203F-\\u2040]");
65125         var tagNamePattern = new RegExp('^'+nameStartChar.source+nameChar.source+'*(?:\:'+nameStartChar.source+nameChar.source+'*)?$');
65126         //var tagNamePattern = /^[a-zA-Z_][\w\-\.]*(?:\:[a-zA-Z_][\w\-\.]*)?$/
65127         //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(',')
65128
65129         //S_TAG,        S_ATTR, S_EQ,   S_ATTR_NOQUOT_VALUE
65130         //S_ATTR_SPACE, S_ATTR_END,     S_TAG_SPACE, S_TAG_CLOSE
65131         var S_TAG = 0;//tag name offerring
65132         var S_ATTR = 1;//attr name offerring 
65133         var S_ATTR_SPACE=2;//attr name end and space offer
65134         var S_EQ = 3;//=space?
65135         var S_ATTR_NOQUOT_VALUE = 4;//attr value(no quot value only)
65136         var S_ATTR_END = 5;//attr value end and no space(quot end)
65137         var S_TAG_SPACE = 6;//(attr value end || tag end ) && (space offer)
65138         var S_TAG_CLOSE = 7;//closed el<el />
65139
65140         function XMLReader(){
65141                 
65142         }
65143
65144         XMLReader.prototype = {
65145                 parse:function(source,defaultNSMap,entityMap){
65146                         var domBuilder = this.domBuilder;
65147                         domBuilder.startDocument();
65148                         _copy(defaultNSMap ,defaultNSMap = {});
65149                         parse(source,defaultNSMap,entityMap,
65150                                         domBuilder,this.errorHandler);
65151                         domBuilder.endDocument();
65152                 }
65153         };
65154         function parse(source,defaultNSMapCopy,entityMap,domBuilder,errorHandler){
65155                 function fixedFromCharCode(code) {
65156                         // String.prototype.fromCharCode does not supports
65157                         // > 2 bytes unicode chars directly
65158                         if (code > 0xffff) {
65159                                 code -= 0x10000;
65160                                 var surrogate1 = 0xd800 + (code >> 10)
65161                                         , surrogate2 = 0xdc00 + (code & 0x3ff);
65162
65163                                 return String.fromCharCode(surrogate1, surrogate2);
65164                         } else {
65165                                 return String.fromCharCode(code);
65166                         }
65167                 }
65168                 function entityReplacer(a){
65169                         var k = a.slice(1,-1);
65170                         if(k in entityMap){
65171                                 return entityMap[k]; 
65172                         }else if(k.charAt(0) === '#'){
65173                                 return fixedFromCharCode(parseInt(k.substr(1).replace('x','0x')))
65174                         }else {
65175                                 errorHandler.error('entity not found:'+a);
65176                                 return a;
65177                         }
65178                 }
65179                 function appendText(end){//has some bugs
65180                         if(end>start){
65181                                 var xt = source.substring(start,end).replace(/&#?\w+;/g,entityReplacer);
65182                                 locator&&position(start);
65183                                 domBuilder.characters(xt,0,end-start);
65184                                 start = end;
65185                         }
65186                 }
65187                 function position(p,m){
65188                         while(p>=lineEnd && (m = linePattern.exec(source))){
65189                                 lineStart = m.index;
65190                                 lineEnd = lineStart + m[0].length;
65191                                 locator.lineNumber++;
65192                                 //console.log('line++:',locator,startPos,endPos)
65193                         }
65194                         locator.columnNumber = p-lineStart+1;
65195                 }
65196                 var lineStart = 0;
65197                 var lineEnd = 0;
65198                 var linePattern = /.*(?:\r\n?|\n)|.*$/g;
65199                 var locator = domBuilder.locator;
65200                 
65201                 var parseStack = [{currentNSMap:defaultNSMapCopy}];
65202                 var closeMap = {};
65203                 var start = 0;
65204                 while(true){
65205                         try{
65206                                 var tagStart = source.indexOf('<',start);
65207                                 if(tagStart<0){
65208                                         if(!source.substr(start).match(/^\s*$/)){
65209                                                 var doc = domBuilder.doc;
65210                                         var text = doc.createTextNode(source.substr(start));
65211                                         doc.appendChild(text);
65212                                         domBuilder.currentElement = text;
65213                                         }
65214                                         return;
65215                                 }
65216                                 if(tagStart>start){
65217                                         appendText(tagStart);
65218                                 }
65219                                 switch(source.charAt(tagStart+1)){
65220                                 case '/':
65221                                         var end = source.indexOf('>',tagStart+3);
65222                                         var tagName = source.substring(tagStart+2,end);
65223                                         var config = parseStack.pop();
65224                                         if(end<0){
65225                                                 
65226                                         tagName = source.substring(tagStart+2).replace(/[\s<].*/,'');
65227                                         //console.error('#@@@@@@'+tagName)
65228                                         errorHandler.error("end tag name: "+tagName+' is not complete:'+config.tagName);
65229                                         end = tagStart+1+tagName.length;
65230                                 }else if(tagName.match(/\s</)){
65231                                         tagName = tagName.replace(/[\s<].*/,'');
65232                                         errorHandler.error("end tag name: "+tagName+' maybe not complete');
65233                                         end = tagStart+1+tagName.length;
65234                                         }
65235                                         //console.error(parseStack.length,parseStack)
65236                                         //console.error(config);
65237                                         var localNSMap = config.localNSMap;
65238                                         var endMatch = config.tagName == tagName;
65239                                         var endIgnoreCaseMach = endMatch || config.tagName&&config.tagName.toLowerCase() == tagName.toLowerCase();
65240                                 if(endIgnoreCaseMach){
65241                                         domBuilder.endElement(config.uri,config.localName,tagName);
65242                                                 if(localNSMap){
65243                                                         for(var prefix in localNSMap){
65244                                                                 domBuilder.endPrefixMapping(prefix) ;
65245                                                         }
65246                                                 }
65247                                                 if(!endMatch){
65248                                         errorHandler.fatalError("end tag name: "+tagName+' is not match the current start tagName:'+config.tagName );
65249                                                 }
65250                                 }else {
65251                                         parseStack.push(config);
65252                                 }
65253                                         
65254                                         end++;
65255                                         break;
65256                                         // end elment
65257                                 case '?':// <?...?>
65258                                         locator&&position(tagStart);
65259                                         end = parseInstruction(source,tagStart,domBuilder);
65260                                         break;
65261                                 case '!':// <!doctype,<![CDATA,<!--
65262                                         locator&&position(tagStart);
65263                                         end = parseDCC(source,tagStart,domBuilder,errorHandler);
65264                                         break;
65265                                 default:
65266                                         locator&&position(tagStart);
65267                                         var el = new ElementAttributes();
65268                                         var currentNSMap = parseStack[parseStack.length-1].currentNSMap;
65269                                         //elStartEnd
65270                                         var end = parseElementStartPart(source,tagStart,el,currentNSMap,entityReplacer,errorHandler);
65271                                         var len = el.length;
65272                                         
65273                                         
65274                                         if(!el.closed && fixSelfClosed(source,end,el.tagName,closeMap)){
65275                                                 el.closed = true;
65276                                                 if(!entityMap.nbsp){
65277                                                         errorHandler.warning('unclosed xml attribute');
65278                                                 }
65279                                         }
65280                                         if(locator && len){
65281                                                 var locator2 = copyLocator(locator,{});
65282                                                 //try{//attribute position fixed
65283                                                 for(var i = 0;i<len;i++){
65284                                                         var a = el[i];
65285                                                         position(a.offset);
65286                                                         a.locator = copyLocator(locator,{});
65287                                                 }
65288                                                 //}catch(e){console.error('@@@@@'+e)}
65289                                                 domBuilder.locator = locator2;
65290                                                 if(appendElement(el,domBuilder,currentNSMap)){
65291                                                         parseStack.push(el);
65292                                                 }
65293                                                 domBuilder.locator = locator;
65294                                         }else {
65295                                                 if(appendElement(el,domBuilder,currentNSMap)){
65296                                                         parseStack.push(el);
65297                                                 }
65298                                         }
65299                                         
65300                                         
65301                                         
65302                                         if(el.uri === 'http://www.w3.org/1999/xhtml' && !el.closed){
65303                                                 end = parseHtmlSpecialContent(source,end,el.tagName,entityReplacer,domBuilder);
65304                                         }else {
65305                                                 end++;
65306                                         }
65307                                 }
65308                         }catch(e){
65309                                 errorHandler.error('element parse error: '+e);
65310                                 //errorHandler.error('element parse error: '+e);
65311                                 end = -1;
65312                                 //throw e;
65313                         }
65314                         if(end>start){
65315                                 start = end;
65316                         }else {
65317                                 //TODO: 这里有可能sax回退,有位置错误风险
65318                                 appendText(Math.max(tagStart,start)+1);
65319                         }
65320                 }
65321         }
65322         function copyLocator(f,t){
65323                 t.lineNumber = f.lineNumber;
65324                 t.columnNumber = f.columnNumber;
65325                 return t;
65326         }
65327
65328         /**
65329          * @see #appendElement(source,elStartEnd,el,selfClosed,entityReplacer,domBuilder,parseStack);
65330          * @return end of the elementStartPart(end of elementEndPart for selfClosed el)
65331          */
65332         function parseElementStartPart(source,start,el,currentNSMap,entityReplacer,errorHandler){
65333                 var attrName;
65334                 var value;
65335                 var p = ++start;
65336                 var s = S_TAG;//status
65337                 while(true){
65338                         var c = source.charAt(p);
65339                         switch(c){
65340                         case '=':
65341                                 if(s === S_ATTR){//attrName
65342                                         attrName = source.slice(start,p);
65343                                         s = S_EQ;
65344                                 }else if(s === S_ATTR_SPACE){
65345                                         s = S_EQ;
65346                                 }else {
65347                                         //fatalError: equal must after attrName or space after attrName
65348                                         throw new Error('attribute equal must after attrName');
65349                                 }
65350                                 break;
65351                         case '\'':
65352                         case '"':
65353                                 if(s === S_EQ || s === S_ATTR //|| s == S_ATTR_SPACE
65354                                         ){//equal
65355                                         if(s === S_ATTR){
65356                                                 errorHandler.warning('attribute value must after "="');
65357                                                 attrName = source.slice(start,p);
65358                                         }
65359                                         start = p+1;
65360                                         p = source.indexOf(c,start);
65361                                         if(p>0){
65362                                                 value = source.slice(start,p).replace(/&#?\w+;/g,entityReplacer);
65363                                                 el.add(attrName,value,start-1);
65364                                                 s = S_ATTR_END;
65365                                         }else {
65366                                                 //fatalError: no end quot match
65367                                                 throw new Error('attribute value no end \''+c+'\' match');
65368                                         }
65369                                 }else if(s == S_ATTR_NOQUOT_VALUE){
65370                                         value = source.slice(start,p).replace(/&#?\w+;/g,entityReplacer);
65371                                         //console.log(attrName,value,start,p)
65372                                         el.add(attrName,value,start);
65373                                         //console.dir(el)
65374                                         errorHandler.warning('attribute "'+attrName+'" missed start quot('+c+')!!');
65375                                         start = p+1;
65376                                         s = S_ATTR_END;
65377                                 }else {
65378                                         //fatalError: no equal before
65379                                         throw new Error('attribute value must after "="');
65380                                 }
65381                                 break;
65382                         case '/':
65383                                 switch(s){
65384                                 case S_TAG:
65385                                         el.setTagName(source.slice(start,p));
65386                                 case S_ATTR_END:
65387                                 case S_TAG_SPACE:
65388                                 case S_TAG_CLOSE:
65389                                         s =S_TAG_CLOSE;
65390                                         el.closed = true;
65391                                 case S_ATTR_NOQUOT_VALUE:
65392                                 case S_ATTR:
65393                                 case S_ATTR_SPACE:
65394                                         break;
65395                                 //case S_EQ:
65396                                 default:
65397                                         throw new Error("attribute invalid close char('/')")
65398                                 }
65399                                 break;
65400                         case ''://end document
65401                                 //throw new Error('unexpected end of input')
65402                                 errorHandler.error('unexpected end of input');
65403                                 if(s == S_TAG){
65404                                         el.setTagName(source.slice(start,p));
65405                                 }
65406                                 return p;
65407                         case '>':
65408                                 switch(s){
65409                                 case S_TAG:
65410                                         el.setTagName(source.slice(start,p));
65411                                 case S_ATTR_END:
65412                                 case S_TAG_SPACE:
65413                                 case S_TAG_CLOSE:
65414                                         break;//normal
65415                                 case S_ATTR_NOQUOT_VALUE://Compatible state
65416                                 case S_ATTR:
65417                                         value = source.slice(start,p);
65418                                         if(value.slice(-1) === '/'){
65419                                                 el.closed  = true;
65420                                                 value = value.slice(0,-1);
65421                                         }
65422                                 case S_ATTR_SPACE:
65423                                         if(s === S_ATTR_SPACE){
65424                                                 value = attrName;
65425                                         }
65426                                         if(s == S_ATTR_NOQUOT_VALUE){
65427                                                 errorHandler.warning('attribute "'+value+'" missed quot(")!!');
65428                                                 el.add(attrName,value.replace(/&#?\w+;/g,entityReplacer),start);
65429                                         }else {
65430                                                 if(currentNSMap[''] !== 'http://www.w3.org/1999/xhtml' || !value.match(/^(?:disabled|checked|selected)$/i)){
65431                                                         errorHandler.warning('attribute "'+value+'" missed value!! "'+value+'" instead!!');
65432                                                 }
65433                                                 el.add(value,value,start);
65434                                         }
65435                                         break;
65436                                 case S_EQ:
65437                                         throw new Error('attribute value missed!!');
65438                                 }
65439         //                      console.log(tagName,tagNamePattern,tagNamePattern.test(tagName))
65440                                 return p;
65441                         /*xml space '\x20' | #x9 | #xD | #xA; */
65442                         case '\u0080':
65443                                 c = ' ';
65444                         default:
65445                                 if(c<= ' '){//space
65446                                         switch(s){
65447                                         case S_TAG:
65448                                                 el.setTagName(source.slice(start,p));//tagName
65449                                                 s = S_TAG_SPACE;
65450                                                 break;
65451                                         case S_ATTR:
65452                                                 attrName = source.slice(start,p);
65453                                                 s = S_ATTR_SPACE;
65454                                                 break;
65455                                         case S_ATTR_NOQUOT_VALUE:
65456                                                 var value = source.slice(start,p).replace(/&#?\w+;/g,entityReplacer);
65457                                                 errorHandler.warning('attribute "'+value+'" missed quot(")!!');
65458                                                 el.add(attrName,value,start);
65459                                         case S_ATTR_END:
65460                                                 s = S_TAG_SPACE;
65461                                                 break;
65462                                         //case S_TAG_SPACE:
65463                                         //case S_EQ:
65464                                         //case S_ATTR_SPACE:
65465                                         //      void();break;
65466                                         //case S_TAG_CLOSE:
65467                                                 //ignore warning
65468                                         }
65469                                 }else {//not space
65470         //S_TAG,        S_ATTR, S_EQ,   S_ATTR_NOQUOT_VALUE
65471         //S_ATTR_SPACE, S_ATTR_END,     S_TAG_SPACE, S_TAG_CLOSE
65472                                         switch(s){
65473                                         //case S_TAG:void();break;
65474                                         //case S_ATTR:void();break;
65475                                         //case S_ATTR_NOQUOT_VALUE:void();break;
65476                                         case S_ATTR_SPACE:
65477                                                 var tagName =  el.tagName;
65478                                                 if(currentNSMap[''] !== 'http://www.w3.org/1999/xhtml' || !attrName.match(/^(?:disabled|checked|selected)$/i)){
65479                                                         errorHandler.warning('attribute "'+attrName+'" missed value!! "'+attrName+'" instead2!!');
65480                                                 }
65481                                                 el.add(attrName,attrName,start);
65482                                                 start = p;
65483                                                 s = S_ATTR;
65484                                                 break;
65485                                         case S_ATTR_END:
65486                                                 errorHandler.warning('attribute space is required"'+attrName+'"!!');
65487                                         case S_TAG_SPACE:
65488                                                 s = S_ATTR;
65489                                                 start = p;
65490                                                 break;
65491                                         case S_EQ:
65492                                                 s = S_ATTR_NOQUOT_VALUE;
65493                                                 start = p;
65494                                                 break;
65495                                         case S_TAG_CLOSE:
65496                                                 throw new Error("elements closed character '/' and '>' must be connected to");
65497                                         }
65498                                 }
65499                         }//end outer switch
65500                         //console.log('p++',p)
65501                         p++;
65502                 }
65503         }
65504         /**
65505          * @return true if has new namespace define
65506          */
65507         function appendElement(el,domBuilder,currentNSMap){
65508                 var tagName = el.tagName;
65509                 var localNSMap = null;
65510                 //var currentNSMap = parseStack[parseStack.length-1].currentNSMap;
65511                 var i = el.length;
65512                 while(i--){
65513                         var a = el[i];
65514                         var qName = a.qName;
65515                         var value = a.value;
65516                         var nsp = qName.indexOf(':');
65517                         if(nsp>0){
65518                                 var prefix = a.prefix = qName.slice(0,nsp);
65519                                 var localName = qName.slice(nsp+1);
65520                                 var nsPrefix = prefix === 'xmlns' && localName;
65521                         }else {
65522                                 localName = qName;
65523                                 prefix = null;
65524                                 nsPrefix = qName === 'xmlns' && '';
65525                         }
65526                         //can not set prefix,because prefix !== ''
65527                         a.localName = localName ;
65528                         //prefix == null for no ns prefix attribute 
65529                         if(nsPrefix !== false){//hack!!
65530                                 if(localNSMap == null){
65531                                         localNSMap = {};
65532                                         //console.log(currentNSMap,0)
65533                                         _copy(currentNSMap,currentNSMap={});
65534                                         //console.log(currentNSMap,1)
65535                                 }
65536                                 currentNSMap[nsPrefix] = localNSMap[nsPrefix] = value;
65537                                 a.uri = 'http://www.w3.org/2000/xmlns/';
65538                                 domBuilder.startPrefixMapping(nsPrefix, value); 
65539                         }
65540                 }
65541                 var i = el.length;
65542                 while(i--){
65543                         a = el[i];
65544                         var prefix = a.prefix;
65545                         if(prefix){//no prefix attribute has no namespace
65546                                 if(prefix === 'xml'){
65547                                         a.uri = 'http://www.w3.org/XML/1998/namespace';
65548                                 }if(prefix !== 'xmlns'){
65549                                         a.uri = currentNSMap[prefix || ''];
65550                                         
65551                                         //{console.log('###'+a.qName,domBuilder.locator.systemId+'',currentNSMap,a.uri)}
65552                                 }
65553                         }
65554                 }
65555                 var nsp = tagName.indexOf(':');
65556                 if(nsp>0){
65557                         prefix = el.prefix = tagName.slice(0,nsp);
65558                         localName = el.localName = tagName.slice(nsp+1);
65559                 }else {
65560                         prefix = null;//important!!
65561                         localName = el.localName = tagName;
65562                 }
65563                 //no prefix element has default namespace
65564                 var ns = el.uri = currentNSMap[prefix || ''];
65565                 domBuilder.startElement(ns,localName,tagName,el);
65566                 //endPrefixMapping and startPrefixMapping have not any help for dom builder
65567                 //localNSMap = null
65568                 if(el.closed){
65569                         domBuilder.endElement(ns,localName,tagName);
65570                         if(localNSMap){
65571                                 for(prefix in localNSMap){
65572                                         domBuilder.endPrefixMapping(prefix); 
65573                                 }
65574                         }
65575                 }else {
65576                         el.currentNSMap = currentNSMap;
65577                         el.localNSMap = localNSMap;
65578                         //parseStack.push(el);
65579                         return true;
65580                 }
65581         }
65582         function parseHtmlSpecialContent(source,elStartEnd,tagName,entityReplacer,domBuilder){
65583                 if(/^(?:script|textarea)$/i.test(tagName)){
65584                         var elEndStart =  source.indexOf('</'+tagName+'>',elStartEnd);
65585                         var text = source.substring(elStartEnd+1,elEndStart);
65586                         if(/[&<]/.test(text)){
65587                                 if(/^script$/i.test(tagName)){
65588                                         //if(!/\]\]>/.test(text)){
65589                                                 //lexHandler.startCDATA();
65590                                                 domBuilder.characters(text,0,text.length);
65591                                                 //lexHandler.endCDATA();
65592                                                 return elEndStart;
65593                                         //}
65594                                 }//}else{//text area
65595                                         text = text.replace(/&#?\w+;/g,entityReplacer);
65596                                         domBuilder.characters(text,0,text.length);
65597                                         return elEndStart;
65598                                 //}
65599                                 
65600                         }
65601                 }
65602                 return elStartEnd+1;
65603         }
65604         function fixSelfClosed(source,elStartEnd,tagName,closeMap){
65605                 //if(tagName in closeMap){
65606                 var pos = closeMap[tagName];
65607                 if(pos == null){
65608                         //console.log(tagName)
65609                         pos =  source.lastIndexOf('</'+tagName+'>');
65610                         if(pos<elStartEnd){//忘记闭合
65611                                 pos = source.lastIndexOf('</'+tagName);
65612                         }
65613                         closeMap[tagName] =pos;
65614                 }
65615                 return pos<elStartEnd;
65616                 //} 
65617         }
65618         function _copy(source,target){
65619                 for(var n in source){target[n] = source[n];}
65620         }
65621         function parseDCC(source,start,domBuilder,errorHandler){//sure start with '<!'
65622                 var next= source.charAt(start+2);
65623                 switch(next){
65624                 case '-':
65625                         if(source.charAt(start + 3) === '-'){
65626                                 var end = source.indexOf('-->',start+4);
65627                                 //append comment source.substring(4,end)//<!--
65628                                 if(end>start){
65629                                         domBuilder.comment(source,start+4,end-start-4);
65630                                         return end+3;
65631                                 }else {
65632                                         errorHandler.error("Unclosed comment");
65633                                         return -1;
65634                                 }
65635                         }else {
65636                                 //error
65637                                 return -1;
65638                         }
65639                 default:
65640                         if(source.substr(start+3,6) == 'CDATA['){
65641                                 var end = source.indexOf(']]>',start+9);
65642                                 domBuilder.startCDATA();
65643                                 domBuilder.characters(source,start+9,end-start-9);
65644                                 domBuilder.endCDATA(); 
65645                                 return end+3;
65646                         }
65647                         //<!DOCTYPE
65648                         //startDTD(java.lang.String name, java.lang.String publicId, java.lang.String systemId) 
65649                         var matchs = split(source,start);
65650                         var len = matchs.length;
65651                         if(len>1 && /!doctype/i.test(matchs[0][0])){
65652                                 var name = matchs[1][0];
65653                                 var pubid = len>3 && /^public$/i.test(matchs[2][0]) && matchs[3][0];
65654                                 var sysid = len>4 && matchs[4][0];
65655                                 var lastMatch = matchs[len-1];
65656                                 domBuilder.startDTD(name,pubid && pubid.replace(/^(['"])(.*?)\1$/,'$2'),
65657                                                 sysid && sysid.replace(/^(['"])(.*?)\1$/,'$2'));
65658                                 domBuilder.endDTD();
65659                                 
65660                                 return lastMatch.index+lastMatch[0].length
65661                         }
65662                 }
65663                 return -1;
65664         }
65665
65666
65667
65668         function parseInstruction(source,start,domBuilder){
65669                 var end = source.indexOf('?>',start);
65670                 if(end){
65671                         var match = source.substring(start,end).match(/^<\?(\S*)\s*([\s\S]*?)\s*$/);
65672                         if(match){
65673                                 var len = match[0].length;
65674                                 domBuilder.processingInstruction(match[1], match[2]) ;
65675                                 return end+2;
65676                         }else {//error
65677                                 return -1;
65678                         }
65679                 }
65680                 return -1;
65681         }
65682
65683         /**
65684          * @param source
65685          */
65686         function ElementAttributes(source){
65687                 
65688         }
65689         ElementAttributes.prototype = {
65690                 setTagName:function(tagName){
65691                         if(!tagNamePattern.test(tagName)){
65692                                 throw new Error('invalid tagName:'+tagName)
65693                         }
65694                         this.tagName = tagName;
65695                 },
65696                 add:function(qName,value,offset){
65697                         if(!tagNamePattern.test(qName)){
65698                                 throw new Error('invalid attribute:'+qName)
65699                         }
65700                         this[this.length++] = {qName:qName,value:value,offset:offset};
65701                 },
65702                 length:0,
65703                 getLocalName:function(i){return this[i].localName},
65704                 getLocator:function(i){return this[i].locator},
65705                 getQName:function(i){return this[i].qName},
65706                 getURI:function(i){return this[i].uri},
65707                 getValue:function(i){return this[i].value}
65708         //      ,getIndex:function(uri, localName)){
65709         //              if(localName){
65710         //                      
65711         //              }else{
65712         //                      var qName = uri
65713         //              }
65714         //      },
65715         //      getValue:function(){return this.getValue(this.getIndex.apply(this,arguments))},
65716         //      getType:function(uri,localName){}
65717         //      getType:function(i){},
65718         };
65719
65720
65721
65722
65723         function _set_proto_(thiz,parent){
65724                 thiz.__proto__ = parent;
65725                 return thiz;
65726         }
65727         if(!(_set_proto_({},_set_proto_.prototype) instanceof _set_proto_)){
65728                 _set_proto_ = function(thiz,parent){
65729                         function p(){}          p.prototype = parent;
65730                         p = new p();
65731                         for(parent in thiz){
65732                                 p[parent] = thiz[parent];
65733                         }
65734                         return p;
65735                 };
65736         }
65737
65738         function split(source,start){
65739                 var match;
65740                 var buf = [];
65741                 var reg = /'[^']+'|"[^"]+"|[^\s<>\/=]+=?|(\/?\s*>|<)/g;
65742                 reg.lastIndex = start;
65743                 reg.exec(source);//skip <
65744                 while(match = reg.exec(source)){
65745                         buf.push(match);
65746                         if(match[1]){ return buf; }
65747                 }
65748         }
65749
65750         var XMLReader_1 = XMLReader;
65751
65752         var sax = {
65753                 XMLReader: XMLReader_1
65754         };
65755
65756         /*
65757          * DOM Level 2
65758          * Object DOMException
65759          * @see http://www.w3.org/TR/REC-DOM-Level-1/ecma-script-language-binding.html
65760          * @see http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/ecma-script-binding.html
65761          */
65762
65763         function copy$2(src,dest){
65764                 for(var p in src){
65765                         dest[p] = src[p];
65766                 }
65767         }
65768         /**
65769         ^\w+\.prototype\.([_\w]+)\s*=\s*((?:.*\{\s*?[\r\n][\s\S]*?^})|\S.*?(?=[;\r\n]));?
65770         ^\w+\.prototype\.([_\w]+)\s*=\s*(\S.*?(?=[;\r\n]));?
65771          */
65772         function _extends(Class,Super){
65773                 var pt = Class.prototype;
65774                 if(Object.create){
65775                         var ppt = Object.create(Super.prototype);
65776                         pt.__proto__ = ppt;
65777                 }
65778                 if(!(pt instanceof Super)){
65779                         function t(){}          t.prototype = Super.prototype;
65780                         t = new t();
65781                         copy$2(pt,t);
65782                         Class.prototype = pt = t;
65783                 }
65784                 if(pt.constructor != Class){
65785                         if(typeof Class != 'function'){
65786                                 console.error("unknow Class:"+Class);
65787                         }
65788                         pt.constructor = Class;
65789                 }
65790         }
65791         var htmlns = 'http://www.w3.org/1999/xhtml' ;
65792         // Node Types
65793         var NodeType = {};
65794         var ELEMENT_NODE                = NodeType.ELEMENT_NODE                = 1;
65795         var ATTRIBUTE_NODE              = NodeType.ATTRIBUTE_NODE              = 2;
65796         var TEXT_NODE                   = NodeType.TEXT_NODE                   = 3;
65797         var CDATA_SECTION_NODE          = NodeType.CDATA_SECTION_NODE          = 4;
65798         var ENTITY_REFERENCE_NODE       = NodeType.ENTITY_REFERENCE_NODE       = 5;
65799         var ENTITY_NODE                 = NodeType.ENTITY_NODE                 = 6;
65800         var PROCESSING_INSTRUCTION_NODE = NodeType.PROCESSING_INSTRUCTION_NODE = 7;
65801         var COMMENT_NODE                = NodeType.COMMENT_NODE                = 8;
65802         var DOCUMENT_NODE               = NodeType.DOCUMENT_NODE               = 9;
65803         var DOCUMENT_TYPE_NODE          = NodeType.DOCUMENT_TYPE_NODE          = 10;
65804         var DOCUMENT_FRAGMENT_NODE      = NodeType.DOCUMENT_FRAGMENT_NODE      = 11;
65805         var NOTATION_NODE               = NodeType.NOTATION_NODE               = 12;
65806
65807         // ExceptionCode
65808         var ExceptionCode = {};
65809         var ExceptionMessage = {};
65810         var INDEX_SIZE_ERR              = ExceptionCode.INDEX_SIZE_ERR              = ((ExceptionMessage[1]="Index size error"),1);
65811         var DOMSTRING_SIZE_ERR          = ExceptionCode.DOMSTRING_SIZE_ERR          = ((ExceptionMessage[2]="DOMString size error"),2);
65812         var HIERARCHY_REQUEST_ERR       = ExceptionCode.HIERARCHY_REQUEST_ERR       = ((ExceptionMessage[3]="Hierarchy request error"),3);
65813         var WRONG_DOCUMENT_ERR          = ExceptionCode.WRONG_DOCUMENT_ERR          = ((ExceptionMessage[4]="Wrong document"),4);
65814         var INVALID_CHARACTER_ERR       = ExceptionCode.INVALID_CHARACTER_ERR       = ((ExceptionMessage[5]="Invalid character"),5);
65815         var NO_DATA_ALLOWED_ERR         = ExceptionCode.NO_DATA_ALLOWED_ERR         = ((ExceptionMessage[6]="No data allowed"),6);
65816         var NO_MODIFICATION_ALLOWED_ERR = ExceptionCode.NO_MODIFICATION_ALLOWED_ERR = ((ExceptionMessage[7]="No modification allowed"),7);
65817         var NOT_FOUND_ERR               = ExceptionCode.NOT_FOUND_ERR               = ((ExceptionMessage[8]="Not found"),8);
65818         var NOT_SUPPORTED_ERR           = ExceptionCode.NOT_SUPPORTED_ERR           = ((ExceptionMessage[9]="Not supported"),9);
65819         var INUSE_ATTRIBUTE_ERR         = ExceptionCode.INUSE_ATTRIBUTE_ERR         = ((ExceptionMessage[10]="Attribute in use"),10);
65820         //level2
65821         var INVALID_STATE_ERR           = ExceptionCode.INVALID_STATE_ERR               = ((ExceptionMessage[11]="Invalid state"),11);
65822         var SYNTAX_ERR                  = ExceptionCode.SYNTAX_ERR                      = ((ExceptionMessage[12]="Syntax error"),12);
65823         var INVALID_MODIFICATION_ERR    = ExceptionCode.INVALID_MODIFICATION_ERR        = ((ExceptionMessage[13]="Invalid modification"),13);
65824         var NAMESPACE_ERR               = ExceptionCode.NAMESPACE_ERR                   = ((ExceptionMessage[14]="Invalid namespace"),14);
65825         var INVALID_ACCESS_ERR          = ExceptionCode.INVALID_ACCESS_ERR              = ((ExceptionMessage[15]="Invalid access"),15);
65826
65827
65828         function DOMException$2(code, message) {
65829                 if(message instanceof Error){
65830                         var error = message;
65831                 }else {
65832                         error = this;
65833                         Error.call(this, ExceptionMessage[code]);
65834                         this.message = ExceptionMessage[code];
65835                         if(Error.captureStackTrace) { Error.captureStackTrace(this, DOMException$2); }
65836                 }
65837                 error.code = code;
65838                 if(message) { this.message = this.message + ": " + message; }
65839                 return error;
65840         }DOMException$2.prototype = Error.prototype;
65841         copy$2(ExceptionCode,DOMException$2);
65842         /**
65843          * @see http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html#ID-536297177
65844          * 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.
65845          * The items in the NodeList are accessible via an integral index, starting from 0.
65846          */
65847         function NodeList() {
65848         }NodeList.prototype = {
65849                 /**
65850                  * The number of nodes in the list. The range of valid child node indices is 0 to length-1 inclusive.
65851                  * @standard level1
65852                  */
65853                 length:0, 
65854                 /**
65855                  * 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.
65856                  * @standard level1
65857                  * @param index  unsigned long 
65858                  *   Index into the collection.
65859                  * @return Node
65860                  *      The node at the indexth position in the NodeList, or null if that is not a valid index. 
65861                  */
65862                 item: function(index) {
65863                         return this[index] || null;
65864                 },
65865                 toString:function(isHTML,nodeFilter){
65866                         for(var buf = [], i = 0;i<this.length;i++){
65867                                 serializeToString(this[i],buf,isHTML,nodeFilter);
65868                         }
65869                         return buf.join('');
65870                 }
65871         };
65872         function LiveNodeList(node,refresh){
65873                 this._node = node;
65874                 this._refresh = refresh;
65875                 _updateLiveList(this);
65876         }
65877         function _updateLiveList(list){
65878                 var inc = list._node._inc || list._node.ownerDocument._inc;
65879                 if(list._inc != inc){
65880                         var ls = list._refresh(list._node);
65881                         //console.log(ls.length)
65882                         __set__(list,'length',ls.length);
65883                         copy$2(ls,list);
65884                         list._inc = inc;
65885                 }
65886         }
65887         LiveNodeList.prototype.item = function(i){
65888                 _updateLiveList(this);
65889                 return this[i];
65890         };
65891
65892         _extends(LiveNodeList,NodeList);
65893         /**
65894          * 
65895          * 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.
65896          * NamedNodeMap objects in the DOM are live.
65897          * used for attributes or DocumentType entities 
65898          */
65899         function NamedNodeMap() {
65900         }
65901         function _findNodeIndex(list,node){
65902                 var i = list.length;
65903                 while(i--){
65904                         if(list[i] === node){return i}
65905                 }
65906         }
65907
65908         function _addNamedNode(el,list,newAttr,oldAttr){
65909                 if(oldAttr){
65910                         list[_findNodeIndex(list,oldAttr)] = newAttr;
65911                 }else {
65912                         list[list.length++] = newAttr;
65913                 }
65914                 if(el){
65915                         newAttr.ownerElement = el;
65916                         var doc = el.ownerDocument;
65917                         if(doc){
65918                                 oldAttr && _onRemoveAttribute(doc,el,oldAttr);
65919                                 _onAddAttribute(doc,el,newAttr);
65920                         }
65921                 }
65922         }
65923         function _removeNamedNode(el,list,attr){
65924                 //console.log('remove attr:'+attr)
65925                 var i = _findNodeIndex(list,attr);
65926                 if(i>=0){
65927                         var lastIndex = list.length-1;
65928                         while(i<lastIndex){
65929                                 list[i] = list[++i];
65930                         }
65931                         list.length = lastIndex;
65932                         if(el){
65933                                 var doc = el.ownerDocument;
65934                                 if(doc){
65935                                         _onRemoveAttribute(doc,el,attr);
65936                                         attr.ownerElement = null;
65937                                 }
65938                         }
65939                 }else {
65940                         throw DOMException$2(NOT_FOUND_ERR,new Error(el.tagName+'@'+attr))
65941                 }
65942         }
65943         NamedNodeMap.prototype = {
65944                 length:0,
65945                 item:NodeList.prototype.item,
65946                 getNamedItem: function(key) {
65947         //              if(key.indexOf(':')>0 || key == 'xmlns'){
65948         //                      return null;
65949         //              }
65950                         //console.log()
65951                         var i = this.length;
65952                         while(i--){
65953                                 var attr = this[i];
65954                                 //console.log(attr.nodeName,key)
65955                                 if(attr.nodeName == key){
65956                                         return attr;
65957                                 }
65958                         }
65959                 },
65960                 setNamedItem: function(attr) {
65961                         var el = attr.ownerElement;
65962                         if(el && el!=this._ownerElement){
65963                                 throw new DOMException$2(INUSE_ATTRIBUTE_ERR);
65964                         }
65965                         var oldAttr = this.getNamedItem(attr.nodeName);
65966                         _addNamedNode(this._ownerElement,this,attr,oldAttr);
65967                         return oldAttr;
65968                 },
65969                 /* returns Node */
65970                 setNamedItemNS: function(attr) {// raises: WRONG_DOCUMENT_ERR,NO_MODIFICATION_ALLOWED_ERR,INUSE_ATTRIBUTE_ERR
65971                         var el = attr.ownerElement, oldAttr;
65972                         if(el && el!=this._ownerElement){
65973                                 throw new DOMException$2(INUSE_ATTRIBUTE_ERR);
65974                         }
65975                         oldAttr = this.getNamedItemNS(attr.namespaceURI,attr.localName);
65976                         _addNamedNode(this._ownerElement,this,attr,oldAttr);
65977                         return oldAttr;
65978                 },
65979
65980                 /* returns Node */
65981                 removeNamedItem: function(key) {
65982                         var attr = this.getNamedItem(key);
65983                         _removeNamedNode(this._ownerElement,this,attr);
65984                         return attr;
65985                         
65986                         
65987                 },// raises: NOT_FOUND_ERR,NO_MODIFICATION_ALLOWED_ERR
65988                 
65989                 //for level2
65990                 removeNamedItemNS:function(namespaceURI,localName){
65991                         var attr = this.getNamedItemNS(namespaceURI,localName);
65992                         _removeNamedNode(this._ownerElement,this,attr);
65993                         return attr;
65994                 },
65995                 getNamedItemNS: function(namespaceURI, localName) {
65996                         var i = this.length;
65997                         while(i--){
65998                                 var node = this[i];
65999                                 if(node.localName == localName && node.namespaceURI == namespaceURI){
66000                                         return node;
66001                                 }
66002                         }
66003                         return null;
66004                 }
66005         };
66006         /**
66007          * @see http://www.w3.org/TR/REC-DOM-Level-1/level-one-core.html#ID-102161490
66008          */
66009         function DOMImplementation(/* Object */ features) {
66010                 this._features = {};
66011                 if (features) {
66012                         for (var feature in features) {
66013                                  this._features = features[feature];
66014                         }
66015                 }
66016         }
66017         DOMImplementation.prototype = {
66018                 hasFeature: function(/* string */ feature, /* string */ version) {
66019                         var versions = this._features[feature.toLowerCase()];
66020                         if (versions && (!version || version in versions)) {
66021                                 return true;
66022                         } else {
66023                                 return false;
66024                         }
66025                 },
66026                 // Introduced in DOM Level 2:
66027                 createDocument:function(namespaceURI,  qualifiedName, doctype){// raises:INVALID_CHARACTER_ERR,NAMESPACE_ERR,WRONG_DOCUMENT_ERR
66028                         var doc = new Document();
66029                         doc.implementation = this;
66030                         doc.childNodes = new NodeList();
66031                         doc.doctype = doctype;
66032                         if(doctype){
66033                                 doc.appendChild(doctype);
66034                         }
66035                         if(qualifiedName){
66036                                 var root = doc.createElementNS(namespaceURI,qualifiedName);
66037                                 doc.appendChild(root);
66038                         }
66039                         return doc;
66040                 },
66041                 // Introduced in DOM Level 2:
66042                 createDocumentType:function(qualifiedName, publicId, systemId){// raises:INVALID_CHARACTER_ERR,NAMESPACE_ERR
66043                         var node = new DocumentType();
66044                         node.name = qualifiedName;
66045                         node.nodeName = qualifiedName;
66046                         node.publicId = publicId;
66047                         node.systemId = systemId;
66048                         // Introduced in DOM Level 2:
66049                         //readonly attribute DOMString        internalSubset;
66050                         
66051                         //TODO:..
66052                         //  readonly attribute NamedNodeMap     entities;
66053                         //  readonly attribute NamedNodeMap     notations;
66054                         return node;
66055                 }
66056         };
66057
66058
66059         /**
66060          * @see http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html#ID-1950641247
66061          */
66062
66063         function Node() {
66064         }
66065         Node.prototype = {
66066                 firstChild : null,
66067                 lastChild : null,
66068                 previousSibling : null,
66069                 nextSibling : null,
66070                 attributes : null,
66071                 parentNode : null,
66072                 childNodes : null,
66073                 ownerDocument : null,
66074                 nodeValue : null,
66075                 namespaceURI : null,
66076                 prefix : null,
66077                 localName : null,
66078                 // Modified in DOM Level 2:
66079                 insertBefore:function(newChild, refChild){//raises 
66080                         return _insertBefore(this,newChild,refChild);
66081                 },
66082                 replaceChild:function(newChild, oldChild){//raises 
66083                         this.insertBefore(newChild,oldChild);
66084                         if(oldChild){
66085                                 this.removeChild(oldChild);
66086                         }
66087                 },
66088                 removeChild:function(oldChild){
66089                         return _removeChild(this,oldChild);
66090                 },
66091                 appendChild:function(newChild){
66092                         return this.insertBefore(newChild,null);
66093                 },
66094                 hasChildNodes:function(){
66095                         return this.firstChild != null;
66096                 },
66097                 cloneNode:function(deep){
66098                         return cloneNode(this.ownerDocument||this,this,deep);
66099                 },
66100                 // Modified in DOM Level 2:
66101                 normalize:function(){
66102                         var child = this.firstChild;
66103                         while(child){
66104                                 var next = child.nextSibling;
66105                                 if(next && next.nodeType == TEXT_NODE && child.nodeType == TEXT_NODE){
66106                                         this.removeChild(next);
66107                                         child.appendData(next.data);
66108                                 }else {
66109                                         child.normalize();
66110                                         child = next;
66111                                 }
66112                         }
66113                 },
66114                 // Introduced in DOM Level 2:
66115                 isSupported:function(feature, version){
66116                         return this.ownerDocument.implementation.hasFeature(feature,version);
66117                 },
66118             // Introduced in DOM Level 2:
66119             hasAttributes:function(){
66120                 return this.attributes.length>0;
66121             },
66122             lookupPrefix:function(namespaceURI){
66123                 var el = this;
66124                 while(el){
66125                         var map = el._nsMap;
66126                         //console.dir(map)
66127                         if(map){
66128                                 for(var n in map){
66129                                         if(map[n] == namespaceURI){
66130                                                 return n;
66131                                         }
66132                                 }
66133                         }
66134                         el = el.nodeType == ATTRIBUTE_NODE?el.ownerDocument : el.parentNode;
66135                 }
66136                 return null;
66137             },
66138             // Introduced in DOM Level 3:
66139             lookupNamespaceURI:function(prefix){
66140                 var el = this;
66141                 while(el){
66142                         var map = el._nsMap;
66143                         //console.dir(map)
66144                         if(map){
66145                                 if(prefix in map){
66146                                         return map[prefix] ;
66147                                 }
66148                         }
66149                         el = el.nodeType == ATTRIBUTE_NODE?el.ownerDocument : el.parentNode;
66150                 }
66151                 return null;
66152             },
66153             // Introduced in DOM Level 3:
66154             isDefaultNamespace:function(namespaceURI){
66155                 var prefix = this.lookupPrefix(namespaceURI);
66156                 return prefix == null;
66157             }
66158         };
66159
66160
66161         function _xmlEncoder(c){
66162                 return c == '<' && '&lt;' ||
66163                  c == '>' && '&gt;' ||
66164                  c == '&' && '&amp;' ||
66165                  c == '"' && '&quot;' ||
66166                  '&#'+c.charCodeAt()+';'
66167         }
66168
66169
66170         copy$2(NodeType,Node);
66171         copy$2(NodeType,Node.prototype);
66172
66173         /**
66174          * @param callback return true for continue,false for break
66175          * @return boolean true: break visit;
66176          */
66177         function _visitNode(node,callback){
66178                 if(callback(node)){
66179                         return true;
66180                 }
66181                 if(node = node.firstChild){
66182                         do{
66183                                 if(_visitNode(node,callback)){return true}
66184                 }while(node=node.nextSibling)
66185             }
66186         }
66187
66188
66189
66190         function Document(){
66191         }
66192         function _onAddAttribute(doc,el,newAttr){
66193                 doc && doc._inc++;
66194                 var ns = newAttr.namespaceURI ;
66195                 if(ns == 'http://www.w3.org/2000/xmlns/'){
66196                         //update namespace
66197                         el._nsMap[newAttr.prefix?newAttr.localName:''] = newAttr.value;
66198                 }
66199         }
66200         function _onRemoveAttribute(doc,el,newAttr,remove){
66201                 doc && doc._inc++;
66202                 var ns = newAttr.namespaceURI ;
66203                 if(ns == 'http://www.w3.org/2000/xmlns/'){
66204                         //update namespace
66205                         delete el._nsMap[newAttr.prefix?newAttr.localName:''];
66206                 }
66207         }
66208         function _onUpdateChild(doc,el,newChild){
66209                 if(doc && doc._inc){
66210                         doc._inc++;
66211                         //update childNodes
66212                         var cs = el.childNodes;
66213                         if(newChild){
66214                                 cs[cs.length++] = newChild;
66215                         }else {
66216                                 //console.log(1)
66217                                 var child = el.firstChild;
66218                                 var i = 0;
66219                                 while(child){
66220                                         cs[i++] = child;
66221                                         child =child.nextSibling;
66222                                 }
66223                                 cs.length = i;
66224                         }
66225                 }
66226         }
66227
66228         /**
66229          * attributes;
66230          * children;
66231          * 
66232          * writeable properties:
66233          * nodeValue,Attr:value,CharacterData:data
66234          * prefix
66235          */
66236         function _removeChild(parentNode,child){
66237                 var previous = child.previousSibling;
66238                 var next = child.nextSibling;
66239                 if(previous){
66240                         previous.nextSibling = next;
66241                 }else {
66242                         parentNode.firstChild = next;
66243                 }
66244                 if(next){
66245                         next.previousSibling = previous;
66246                 }else {
66247                         parentNode.lastChild = previous;
66248                 }
66249                 _onUpdateChild(parentNode.ownerDocument,parentNode);
66250                 return child;
66251         }
66252         /**
66253          * preformance key(refChild == null)
66254          */
66255         function _insertBefore(parentNode,newChild,nextChild){
66256                 var cp = newChild.parentNode;
66257                 if(cp){
66258                         cp.removeChild(newChild);//remove and update
66259                 }
66260                 if(newChild.nodeType === DOCUMENT_FRAGMENT_NODE){
66261                         var newFirst = newChild.firstChild;
66262                         if (newFirst == null) {
66263                                 return newChild;
66264                         }
66265                         var newLast = newChild.lastChild;
66266                 }else {
66267                         newFirst = newLast = newChild;
66268                 }
66269                 var pre = nextChild ? nextChild.previousSibling : parentNode.lastChild;
66270
66271                 newFirst.previousSibling = pre;
66272                 newLast.nextSibling = nextChild;
66273                 
66274                 
66275                 if(pre){
66276                         pre.nextSibling = newFirst;
66277                 }else {
66278                         parentNode.firstChild = newFirst;
66279                 }
66280                 if(nextChild == null){
66281                         parentNode.lastChild = newLast;
66282                 }else {
66283                         nextChild.previousSibling = newLast;
66284                 }
66285                 do{
66286                         newFirst.parentNode = parentNode;
66287                 }while(newFirst !== newLast && (newFirst= newFirst.nextSibling))
66288                 _onUpdateChild(parentNode.ownerDocument||parentNode,parentNode);
66289                 //console.log(parentNode.lastChild.nextSibling == null)
66290                 if (newChild.nodeType == DOCUMENT_FRAGMENT_NODE) {
66291                         newChild.firstChild = newChild.lastChild = null;
66292                 }
66293                 return newChild;
66294         }
66295         function _appendSingleChild(parentNode,newChild){
66296                 var cp = newChild.parentNode;
66297                 if(cp){
66298                         var pre = parentNode.lastChild;
66299                         cp.removeChild(newChild);//remove and update
66300                         var pre = parentNode.lastChild;
66301                 }
66302                 var pre = parentNode.lastChild;
66303                 newChild.parentNode = parentNode;
66304                 newChild.previousSibling = pre;
66305                 newChild.nextSibling = null;
66306                 if(pre){
66307                         pre.nextSibling = newChild;
66308                 }else {
66309                         parentNode.firstChild = newChild;
66310                 }
66311                 parentNode.lastChild = newChild;
66312                 _onUpdateChild(parentNode.ownerDocument,parentNode,newChild);
66313                 return newChild;
66314                 //console.log("__aa",parentNode.lastChild.nextSibling == null)
66315         }
66316         Document.prototype = {
66317                 //implementation : null,
66318                 nodeName :  '#document',
66319                 nodeType :  DOCUMENT_NODE,
66320                 doctype :  null,
66321                 documentElement :  null,
66322                 _inc : 1,
66323                 
66324                 insertBefore :  function(newChild, refChild){//raises 
66325                         if(newChild.nodeType == DOCUMENT_FRAGMENT_NODE){
66326                                 var child = newChild.firstChild;
66327                                 while(child){
66328                                         var next = child.nextSibling;
66329                                         this.insertBefore(child,refChild);
66330                                         child = next;
66331                                 }
66332                                 return newChild;
66333                         }
66334                         if(this.documentElement == null && newChild.nodeType == ELEMENT_NODE){
66335                                 this.documentElement = newChild;
66336                         }
66337                         
66338                         return _insertBefore(this,newChild,refChild),(newChild.ownerDocument = this),newChild;
66339                 },
66340                 removeChild :  function(oldChild){
66341                         if(this.documentElement == oldChild){
66342                                 this.documentElement = null;
66343                         }
66344                         return _removeChild(this,oldChild);
66345                 },
66346                 // Introduced in DOM Level 2:
66347                 importNode : function(importedNode,deep){
66348                         return importNode(this,importedNode,deep);
66349                 },
66350                 // Introduced in DOM Level 2:
66351                 getElementById :        function(id){
66352                         var rtv = null;
66353                         _visitNode(this.documentElement,function(node){
66354                                 if(node.nodeType == ELEMENT_NODE){
66355                                         if(node.getAttribute('id') == id){
66356                                                 rtv = node;
66357                                                 return true;
66358                                         }
66359                                 }
66360                         });
66361                         return rtv;
66362                 },
66363                 
66364                 //document factory method:
66365                 createElement : function(tagName){
66366                         var node = new Element();
66367                         node.ownerDocument = this;
66368                         node.nodeName = tagName;
66369                         node.tagName = tagName;
66370                         node.childNodes = new NodeList();
66371                         var attrs       = node.attributes = new NamedNodeMap();
66372                         attrs._ownerElement = node;
66373                         return node;
66374                 },
66375                 createDocumentFragment :        function(){
66376                         var node = new DocumentFragment();
66377                         node.ownerDocument = this;
66378                         node.childNodes = new NodeList();
66379                         return node;
66380                 },
66381                 createTextNode :        function(data){
66382                         var node = new Text();
66383                         node.ownerDocument = this;
66384                         node.appendData(data);
66385                         return node;
66386                 },
66387                 createComment : function(data){
66388                         var node = new Comment();
66389                         node.ownerDocument = this;
66390                         node.appendData(data);
66391                         return node;
66392                 },
66393                 createCDATASection :    function(data){
66394                         var node = new CDATASection();
66395                         node.ownerDocument = this;
66396                         node.appendData(data);
66397                         return node;
66398                 },
66399                 createProcessingInstruction :   function(target,data){
66400                         var node = new ProcessingInstruction();
66401                         node.ownerDocument = this;
66402                         node.tagName = node.target = target;
66403                         node.nodeValue= node.data = data;
66404                         return node;
66405                 },
66406                 createAttribute :       function(name){
66407                         var node = new Attr();
66408                         node.ownerDocument      = this;
66409                         node.name = name;
66410                         node.nodeName   = name;
66411                         node.localName = name;
66412                         node.specified = true;
66413                         return node;
66414                 },
66415                 createEntityReference : function(name){
66416                         var node = new EntityReference();
66417                         node.ownerDocument      = this;
66418                         node.nodeName   = name;
66419                         return node;
66420                 },
66421                 // Introduced in DOM Level 2:
66422                 createElementNS :       function(namespaceURI,qualifiedName){
66423                         var node = new Element();
66424                         var pl = qualifiedName.split(':');
66425                         var attrs       = node.attributes = new NamedNodeMap();
66426                         node.childNodes = new NodeList();
66427                         node.ownerDocument = this;
66428                         node.nodeName = qualifiedName;
66429                         node.tagName = qualifiedName;
66430                         node.namespaceURI = namespaceURI;
66431                         if(pl.length == 2){
66432                                 node.prefix = pl[0];
66433                                 node.localName = pl[1];
66434                         }else {
66435                                 //el.prefix = null;
66436                                 node.localName = qualifiedName;
66437                         }
66438                         attrs._ownerElement = node;
66439                         return node;
66440                 },
66441                 // Introduced in DOM Level 2:
66442                 createAttributeNS :     function(namespaceURI,qualifiedName){
66443                         var node = new Attr();
66444                         var pl = qualifiedName.split(':');
66445                         node.ownerDocument = this;
66446                         node.nodeName = qualifiedName;
66447                         node.name = qualifiedName;
66448                         node.namespaceURI = namespaceURI;
66449                         node.specified = true;
66450                         if(pl.length == 2){
66451                                 node.prefix = pl[0];
66452                                 node.localName = pl[1];
66453                         }else {
66454                                 //el.prefix = null;
66455                                 node.localName = qualifiedName;
66456                         }
66457                         return node;
66458                 }
66459         };
66460         _extends(Document,Node);
66461
66462
66463         function Element() {
66464                 this._nsMap = {};
66465         }Element.prototype = {
66466                 nodeType : ELEMENT_NODE,
66467                 hasAttribute : function(name){
66468                         return this.getAttributeNode(name)!=null;
66469                 },
66470                 getAttribute : function(name){
66471                         var attr = this.getAttributeNode(name);
66472                         return attr && attr.value || '';
66473                 },
66474                 getAttributeNode : function(name){
66475                         return this.attributes.getNamedItem(name);
66476                 },
66477                 setAttribute : function(name, value){
66478                         var attr = this.ownerDocument.createAttribute(name);
66479                         attr.value = attr.nodeValue = "" + value;
66480                         this.setAttributeNode(attr);
66481                 },
66482                 removeAttribute : function(name){
66483                         var attr = this.getAttributeNode(name);
66484                         attr && this.removeAttributeNode(attr);
66485                 },
66486                 
66487                 //four real opeartion method
66488                 appendChild:function(newChild){
66489                         if(newChild.nodeType === DOCUMENT_FRAGMENT_NODE){
66490                                 return this.insertBefore(newChild,null);
66491                         }else {
66492                                 return _appendSingleChild(this,newChild);
66493                         }
66494                 },
66495                 setAttributeNode : function(newAttr){
66496                         return this.attributes.setNamedItem(newAttr);
66497                 },
66498                 setAttributeNodeNS : function(newAttr){
66499                         return this.attributes.setNamedItemNS(newAttr);
66500                 },
66501                 removeAttributeNode : function(oldAttr){
66502                         //console.log(this == oldAttr.ownerElement)
66503                         return this.attributes.removeNamedItem(oldAttr.nodeName);
66504                 },
66505                 //get real attribute name,and remove it by removeAttributeNode
66506                 removeAttributeNS : function(namespaceURI, localName){
66507                         var old = this.getAttributeNodeNS(namespaceURI, localName);
66508                         old && this.removeAttributeNode(old);
66509                 },
66510                 
66511                 hasAttributeNS : function(namespaceURI, localName){
66512                         return this.getAttributeNodeNS(namespaceURI, localName)!=null;
66513                 },
66514                 getAttributeNS : function(namespaceURI, localName){
66515                         var attr = this.getAttributeNodeNS(namespaceURI, localName);
66516                         return attr && attr.value || '';
66517                 },
66518                 setAttributeNS : function(namespaceURI, qualifiedName, value){
66519                         var attr = this.ownerDocument.createAttributeNS(namespaceURI, qualifiedName);
66520                         attr.value = attr.nodeValue = "" + value;
66521                         this.setAttributeNode(attr);
66522                 },
66523                 getAttributeNodeNS : function(namespaceURI, localName){
66524                         return this.attributes.getNamedItemNS(namespaceURI, localName);
66525                 },
66526                 
66527                 getElementsByTagName : function(tagName){
66528                         return new LiveNodeList(this,function(base){
66529                                 var ls = [];
66530                                 _visitNode(base,function(node){
66531                                         if(node !== base && node.nodeType == ELEMENT_NODE && (tagName === '*' || node.tagName == tagName)){
66532                                                 ls.push(node);
66533                                         }
66534                                 });
66535                                 return ls;
66536                         });
66537                 },
66538                 getElementsByTagNameNS : function(namespaceURI, localName){
66539                         return new LiveNodeList(this,function(base){
66540                                 var ls = [];
66541                                 _visitNode(base,function(node){
66542                                         if(node !== base && node.nodeType === ELEMENT_NODE && (namespaceURI === '*' || node.namespaceURI === namespaceURI) && (localName === '*' || node.localName == localName)){
66543                                                 ls.push(node);
66544                                         }
66545                                 });
66546                                 return ls;
66547                                 
66548                         });
66549                 }
66550         };
66551         Document.prototype.getElementsByTagName = Element.prototype.getElementsByTagName;
66552         Document.prototype.getElementsByTagNameNS = Element.prototype.getElementsByTagNameNS;
66553
66554
66555         _extends(Element,Node);
66556         function Attr() {
66557         }Attr.prototype.nodeType = ATTRIBUTE_NODE;
66558         _extends(Attr,Node);
66559
66560
66561         function CharacterData() {
66562         }CharacterData.prototype = {
66563                 data : '',
66564                 substringData : function(offset, count) {
66565                         return this.data.substring(offset, offset+count);
66566                 },
66567                 appendData: function(text) {
66568                         text = this.data+text;
66569                         this.nodeValue = this.data = text;
66570                         this.length = text.length;
66571                 },
66572                 insertData: function(offset,text) {
66573                         this.replaceData(offset,0,text);
66574                 
66575                 },
66576                 appendChild:function(newChild){
66577                         throw new Error(ExceptionMessage[HIERARCHY_REQUEST_ERR])
66578                 },
66579                 deleteData: function(offset, count) {
66580                         this.replaceData(offset,count,"");
66581                 },
66582                 replaceData: function(offset, count, text) {
66583                         var start = this.data.substring(0,offset);
66584                         var end = this.data.substring(offset+count);
66585                         text = start + text + end;
66586                         this.nodeValue = this.data = text;
66587                         this.length = text.length;
66588                 }
66589         };
66590         _extends(CharacterData,Node);
66591         function Text() {
66592         }Text.prototype = {
66593                 nodeName : "#text",
66594                 nodeType : TEXT_NODE,
66595                 splitText : function(offset) {
66596                         var text = this.data;
66597                         var newText = text.substring(offset);
66598                         text = text.substring(0, offset);
66599                         this.data = this.nodeValue = text;
66600                         this.length = text.length;
66601                         var newNode = this.ownerDocument.createTextNode(newText);
66602                         if(this.parentNode){
66603                                 this.parentNode.insertBefore(newNode, this.nextSibling);
66604                         }
66605                         return newNode;
66606                 }
66607         };
66608         _extends(Text,CharacterData);
66609         function Comment() {
66610         }Comment.prototype = {
66611                 nodeName : "#comment",
66612                 nodeType : COMMENT_NODE
66613         };
66614         _extends(Comment,CharacterData);
66615
66616         function CDATASection() {
66617         }CDATASection.prototype = {
66618                 nodeName : "#cdata-section",
66619                 nodeType : CDATA_SECTION_NODE
66620         };
66621         _extends(CDATASection,CharacterData);
66622
66623
66624         function DocumentType() {
66625         }DocumentType.prototype.nodeType = DOCUMENT_TYPE_NODE;
66626         _extends(DocumentType,Node);
66627
66628         function Notation() {
66629         }Notation.prototype.nodeType = NOTATION_NODE;
66630         _extends(Notation,Node);
66631
66632         function Entity() {
66633         }Entity.prototype.nodeType = ENTITY_NODE;
66634         _extends(Entity,Node);
66635
66636         function EntityReference() {
66637         }EntityReference.prototype.nodeType = ENTITY_REFERENCE_NODE;
66638         _extends(EntityReference,Node);
66639
66640         function DocumentFragment() {
66641         }DocumentFragment.prototype.nodeName =  "#document-fragment";
66642         DocumentFragment.prototype.nodeType =   DOCUMENT_FRAGMENT_NODE;
66643         _extends(DocumentFragment,Node);
66644
66645
66646         function ProcessingInstruction() {
66647         }
66648         ProcessingInstruction.prototype.nodeType = PROCESSING_INSTRUCTION_NODE;
66649         _extends(ProcessingInstruction,Node);
66650         function XMLSerializer$1(){}
66651         XMLSerializer$1.prototype.serializeToString = function(node,isHtml,nodeFilter){
66652                 return nodeSerializeToString.call(node,isHtml,nodeFilter);
66653         };
66654         Node.prototype.toString = nodeSerializeToString;
66655         function nodeSerializeToString(isHtml,nodeFilter){
66656                 var buf = [];
66657                 var refNode = this.nodeType == 9?this.documentElement:this;
66658                 var prefix = refNode.prefix;
66659                 var uri = refNode.namespaceURI;
66660                 
66661                 if(uri && prefix == null){
66662                         //console.log(prefix)
66663                         var prefix = refNode.lookupPrefix(uri);
66664                         if(prefix == null){
66665                                 //isHTML = true;
66666                                 var visibleNamespaces=[
66667                                 {namespace:uri,prefix:null} ];
66668                         }
66669                 }
66670                 serializeToString(this,buf,isHtml,nodeFilter,visibleNamespaces);
66671                 //console.log('###',this.nodeType,uri,prefix,buf.join(''))
66672                 return buf.join('');
66673         }
66674         function needNamespaceDefine(node,isHTML, visibleNamespaces) {
66675                 var prefix = node.prefix||'';
66676                 var uri = node.namespaceURI;
66677                 if (!prefix && !uri){
66678                         return false;
66679                 }
66680                 if (prefix === "xml" && uri === "http://www.w3.org/XML/1998/namespace" 
66681                         || uri == 'http://www.w3.org/2000/xmlns/'){
66682                         return false;
66683                 }
66684                 
66685                 var i = visibleNamespaces.length; 
66686                 //console.log('@@@@',node.tagName,prefix,uri,visibleNamespaces)
66687                 while (i--) {
66688                         var ns = visibleNamespaces[i];
66689                         // get namespace prefix
66690                         //console.log(node.nodeType,node.tagName,ns.prefix,prefix)
66691                         if (ns.prefix == prefix){
66692                                 return ns.namespace != uri;
66693                         }
66694                 }
66695                 //console.log(isHTML,uri,prefix=='')
66696                 //if(isHTML && prefix ==null && uri == 'http://www.w3.org/1999/xhtml'){
66697                 //      return false;
66698                 //}
66699                 //node.flag = '11111'
66700                 //console.error(3,true,node.flag,node.prefix,node.namespaceURI)
66701                 return true;
66702         }
66703         function serializeToString(node,buf,isHTML,nodeFilter,visibleNamespaces){
66704                 if(nodeFilter){
66705                         node = nodeFilter(node);
66706                         if(node){
66707                                 if(typeof node == 'string'){
66708                                         buf.push(node);
66709                                         return;
66710                                 }
66711                         }else {
66712                                 return;
66713                         }
66714                         //buf.sort.apply(attrs, attributeSorter);
66715                 }
66716                 switch(node.nodeType){
66717                 case ELEMENT_NODE:
66718                         if (!visibleNamespaces) { visibleNamespaces = []; }
66719                         var startVisibleNamespaces = visibleNamespaces.length;
66720                         var attrs = node.attributes;
66721                         var len = attrs.length;
66722                         var child = node.firstChild;
66723                         var nodeName = node.tagName;
66724                         
66725                         isHTML =  (htmlns === node.namespaceURI) ||isHTML; 
66726                         buf.push('<',nodeName);
66727                         
66728                         
66729                         
66730                         for(var i=0;i<len;i++){
66731                                 // add namespaces for attributes
66732                                 var attr = attrs.item(i);
66733                                 if (attr.prefix == 'xmlns') {
66734                                         visibleNamespaces.push({ prefix: attr.localName, namespace: attr.value });
66735                                 }else if(attr.nodeName == 'xmlns'){
66736                                         visibleNamespaces.push({ prefix: '', namespace: attr.value });
66737                                 }
66738                         }
66739                         for(var i=0;i<len;i++){
66740                                 var attr = attrs.item(i);
66741                                 if (needNamespaceDefine(attr,isHTML, visibleNamespaces)) {
66742                                         var prefix = attr.prefix||'';
66743                                         var uri = attr.namespaceURI;
66744                                         var ns = prefix ? ' xmlns:' + prefix : " xmlns";
66745                                         buf.push(ns, '="' , uri , '"');
66746                                         visibleNamespaces.push({ prefix: prefix, namespace:uri });
66747                                 }
66748                                 serializeToString(attr,buf,isHTML,nodeFilter,visibleNamespaces);
66749                         }
66750                         // add namespace for current node               
66751                         if (needNamespaceDefine(node,isHTML, visibleNamespaces)) {
66752                                 var prefix = node.prefix||'';
66753                                 var uri = node.namespaceURI;
66754                                 var ns = prefix ? ' xmlns:' + prefix : " xmlns";
66755                                 buf.push(ns, '="' , uri , '"');
66756                                 visibleNamespaces.push({ prefix: prefix, namespace:uri });
66757                         }
66758                         
66759                         if(child || isHTML && !/^(?:meta|link|img|br|hr|input)$/i.test(nodeName)){
66760                                 buf.push('>');
66761                                 //if is cdata child node
66762                                 if(isHTML && /^script$/i.test(nodeName)){
66763                                         while(child){
66764                                                 if(child.data){
66765                                                         buf.push(child.data);
66766                                                 }else {
66767                                                         serializeToString(child,buf,isHTML,nodeFilter,visibleNamespaces);
66768                                                 }
66769                                                 child = child.nextSibling;
66770                                         }
66771                                 }else
66772                                 {
66773                                         while(child){
66774                                                 serializeToString(child,buf,isHTML,nodeFilter,visibleNamespaces);
66775                                                 child = child.nextSibling;
66776                                         }
66777                                 }
66778                                 buf.push('</',nodeName,'>');
66779                         }else {
66780                                 buf.push('/>');
66781                         }
66782                         // remove added visible namespaces
66783                         //visibleNamespaces.length = startVisibleNamespaces;
66784                         return;
66785                 case DOCUMENT_NODE:
66786                 case DOCUMENT_FRAGMENT_NODE:
66787                         var child = node.firstChild;
66788                         while(child){
66789                                 serializeToString(child,buf,isHTML,nodeFilter,visibleNamespaces);
66790                                 child = child.nextSibling;
66791                         }
66792                         return;
66793                 case ATTRIBUTE_NODE:
66794                         return buf.push(' ',node.name,'="',node.value.replace(/[<&"]/g,_xmlEncoder),'"');
66795                 case TEXT_NODE:
66796                         return buf.push(node.data.replace(/[<&]/g,_xmlEncoder));
66797                 case CDATA_SECTION_NODE:
66798                         return buf.push( '<![CDATA[',node.data,']]>');
66799                 case COMMENT_NODE:
66800                         return buf.push( "<!--",node.data,"-->");
66801                 case DOCUMENT_TYPE_NODE:
66802                         var pubid = node.publicId;
66803                         var sysid = node.systemId;
66804                         buf.push('<!DOCTYPE ',node.name);
66805                         if(pubid){
66806                                 buf.push(' PUBLIC "',pubid);
66807                                 if (sysid && sysid!='.') {
66808                                         buf.push( '" "',sysid);
66809                                 }
66810                                 buf.push('">');
66811                         }else if(sysid && sysid!='.'){
66812                                 buf.push(' SYSTEM "',sysid,'">');
66813                         }else {
66814                                 var sub = node.internalSubset;
66815                                 if(sub){
66816                                         buf.push(" [",sub,"]");
66817                                 }
66818                                 buf.push(">");
66819                         }
66820                         return;
66821                 case PROCESSING_INSTRUCTION_NODE:
66822                         return buf.push( "<?",node.target," ",node.data,"?>");
66823                 case ENTITY_REFERENCE_NODE:
66824                         return buf.push( '&',node.nodeName,';');
66825                 //case ENTITY_NODE:
66826                 //case NOTATION_NODE:
66827                 default:
66828                         buf.push('??',node.nodeName);
66829                 }
66830         }
66831         function importNode(doc,node,deep){
66832                 var node2;
66833                 switch (node.nodeType) {
66834                 case ELEMENT_NODE:
66835                         node2 = node.cloneNode(false);
66836                         node2.ownerDocument = doc;
66837                         //var attrs = node2.attributes;
66838                         //var len = attrs.length;
66839                         //for(var i=0;i<len;i++){
66840                                 //node2.setAttributeNodeNS(importNode(doc,attrs.item(i),deep));
66841                         //}
66842                 case DOCUMENT_FRAGMENT_NODE:
66843                         break;
66844                 case ATTRIBUTE_NODE:
66845                         deep = true;
66846                         break;
66847                 //case ENTITY_REFERENCE_NODE:
66848                 //case PROCESSING_INSTRUCTION_NODE:
66849                 ////case TEXT_NODE:
66850                 //case CDATA_SECTION_NODE:
66851                 //case COMMENT_NODE:
66852                 //      deep = false;
66853                 //      break;
66854                 //case DOCUMENT_NODE:
66855                 //case DOCUMENT_TYPE_NODE:
66856                 //cannot be imported.
66857                 //case ENTITY_NODE:
66858                 //case NOTATION_NODE:
66859                 //can not hit in level3
66860                 //default:throw e;
66861                 }
66862                 if(!node2){
66863                         node2 = node.cloneNode(false);//false
66864                 }
66865                 node2.ownerDocument = doc;
66866                 node2.parentNode = null;
66867                 if(deep){
66868                         var child = node.firstChild;
66869                         while(child){
66870                                 node2.appendChild(importNode(doc,child,deep));
66871                                 child = child.nextSibling;
66872                         }
66873                 }
66874                 return node2;
66875         }
66876         //
66877         //var _relationMap = {firstChild:1,lastChild:1,previousSibling:1,nextSibling:1,
66878         //                                      attributes:1,childNodes:1,parentNode:1,documentElement:1,doctype,};
66879         function cloneNode(doc,node,deep){
66880                 var node2 = new node.constructor();
66881                 for(var n in node){
66882                         var v = node[n];
66883                         if(typeof v != 'object' ){
66884                                 if(v != node2[n]){
66885                                         node2[n] = v;
66886                                 }
66887                         }
66888                 }
66889                 if(node.childNodes){
66890                         node2.childNodes = new NodeList();
66891                 }
66892                 node2.ownerDocument = doc;
66893                 switch (node2.nodeType) {
66894                 case ELEMENT_NODE:
66895                         var attrs       = node.attributes;
66896                         var attrs2      = node2.attributes = new NamedNodeMap();
66897                         var len = attrs.length;
66898                         attrs2._ownerElement = node2;
66899                         for(var i=0;i<len;i++){
66900                                 node2.setAttributeNode(cloneNode(doc,attrs.item(i),true));
66901                         }
66902                         break;  case ATTRIBUTE_NODE:
66903                         deep = true;
66904                 }
66905                 if(deep){
66906                         var child = node.firstChild;
66907                         while(child){
66908                                 node2.appendChild(cloneNode(doc,child,deep));
66909                                 child = child.nextSibling;
66910                         }
66911                 }
66912                 return node2;
66913         }
66914
66915         function __set__(object,key,value){
66916                 object[key] = value;
66917         }
66918         //do dynamic
66919         try{
66920                 if(Object.defineProperty){
66921                         Object.defineProperty(LiveNodeList.prototype,'length',{
66922                                 get:function(){
66923                                         _updateLiveList(this);
66924                                         return this.$$length;
66925                                 }
66926                         });
66927                         Object.defineProperty(Node.prototype,'textContent',{
66928                                 get:function(){
66929                                         return getTextContent(this);
66930                                 },
66931                                 set:function(data){
66932                                         switch(this.nodeType){
66933                                         case ELEMENT_NODE:
66934                                         case DOCUMENT_FRAGMENT_NODE:
66935                                                 while(this.firstChild){
66936                                                         this.removeChild(this.firstChild);
66937                                                 }
66938                                                 if(data || String(data)){
66939                                                         this.appendChild(this.ownerDocument.createTextNode(data));
66940                                                 }
66941                                                 break;
66942                                         default:
66943                                                 //TODO:
66944                                                 this.data = data;
66945                                                 this.value = data;
66946                                                 this.nodeValue = data;
66947                                         }
66948                                 }
66949                         });
66950                         
66951                         function getTextContent(node){
66952                                 switch(node.nodeType){
66953                                 case ELEMENT_NODE:
66954                                 case DOCUMENT_FRAGMENT_NODE:
66955                                         var buf = [];
66956                                         node = node.firstChild;
66957                                         while(node){
66958                                                 if(node.nodeType!==7 && node.nodeType !==8){
66959                                                         buf.push(getTextContent(node));
66960                                                 }
66961                                                 node = node.nextSibling;
66962                                         }
66963                                         return buf.join('');
66964                                 default:
66965                                         return node.nodeValue;
66966                                 }
66967                         }
66968                         __set__ = function(object,key,value){
66969                                 //console.log(value)
66970                                 object['$$'+key] = value;
66971                         };
66972                 }
66973         }catch(e){//ie8
66974         }
66975
66976         //if(typeof require == 'function'){
66977                 var DOMImplementation_1 = DOMImplementation;
66978                 var XMLSerializer_1 = XMLSerializer$1;
66979         //}
66980
66981         var dom = {
66982                 DOMImplementation: DOMImplementation_1,
66983                 XMLSerializer: XMLSerializer_1
66984         };
66985
66986         var domParser = createCommonjsModule(function (module, exports) {
66987         function DOMParser(options){
66988                 this.options = options ||{locator:{}};
66989                 
66990         }
66991         DOMParser.prototype.parseFromString = function(source,mimeType){
66992                 var options = this.options;
66993                 var sax =  new XMLReader();
66994                 var domBuilder = options.domBuilder || new DOMHandler();//contentHandler and LexicalHandler
66995                 var errorHandler = options.errorHandler;
66996                 var locator = options.locator;
66997                 var defaultNSMap = options.xmlns||{};
66998                 var entityMap = {'lt':'<','gt':'>','amp':'&','quot':'"','apos':"'"};
66999                 if(locator){
67000                         domBuilder.setDocumentLocator(locator);
67001                 }
67002                 
67003                 sax.errorHandler = buildErrorHandler(errorHandler,domBuilder,locator);
67004                 sax.domBuilder = options.domBuilder || domBuilder;
67005                 if(/\/x?html?$/.test(mimeType)){
67006                         entityMap.nbsp = '\xa0';
67007                         entityMap.copy = '\xa9';
67008                         defaultNSMap['']= 'http://www.w3.org/1999/xhtml';
67009                 }
67010                 defaultNSMap.xml = defaultNSMap.xml || 'http://www.w3.org/XML/1998/namespace';
67011                 if(source){
67012                         sax.parse(source,defaultNSMap,entityMap);
67013                 }else {
67014                         sax.errorHandler.error("invalid doc source");
67015                 }
67016                 return domBuilder.doc;
67017         };
67018         function buildErrorHandler(errorImpl,domBuilder,locator){
67019                 if(!errorImpl){
67020                         if(domBuilder instanceof DOMHandler){
67021                                 return domBuilder;
67022                         }
67023                         errorImpl = domBuilder ;
67024                 }
67025                 var errorHandler = {};
67026                 var isCallback = errorImpl instanceof Function;
67027                 locator = locator||{};
67028                 function build(key){
67029                         var fn = errorImpl[key];
67030                         if(!fn && isCallback){
67031                                 fn = errorImpl.length == 2?function(msg){errorImpl(key,msg);}:errorImpl;
67032                         }
67033                         errorHandler[key] = fn && function(msg){
67034                                 fn('[xmldom '+key+']\t'+msg+_locator(locator));
67035                         }||function(){};
67036                 }
67037                 build('warning');
67038                 build('error');
67039                 build('fatalError');
67040                 return errorHandler;
67041         }
67042
67043         //console.log('#\n\n\n\n\n\n\n####')
67044         /**
67045          * +ContentHandler+ErrorHandler
67046          * +LexicalHandler+EntityResolver2
67047          * -DeclHandler-DTDHandler 
67048          * 
67049          * DefaultHandler:EntityResolver, DTDHandler, ContentHandler, ErrorHandler
67050          * DefaultHandler2:DefaultHandler,LexicalHandler, DeclHandler, EntityResolver2
67051          * @link http://www.saxproject.org/apidoc/org/xml/sax/helpers/DefaultHandler.html
67052          */
67053         function DOMHandler() {
67054             this.cdata = false;
67055         }
67056         function position(locator,node){
67057                 node.lineNumber = locator.lineNumber;
67058                 node.columnNumber = locator.columnNumber;
67059         }
67060         /**
67061          * @see org.xml.sax.ContentHandler#startDocument
67062          * @link http://www.saxproject.org/apidoc/org/xml/sax/ContentHandler.html
67063          */ 
67064         DOMHandler.prototype = {
67065                 startDocument : function() {
67066                 this.doc = new DOMImplementation().createDocument(null, null, null);
67067                 if (this.locator) {
67068                         this.doc.documentURI = this.locator.systemId;
67069                 }
67070                 },
67071                 startElement:function(namespaceURI, localName, qName, attrs) {
67072                         var doc = this.doc;
67073                     var el = doc.createElementNS(namespaceURI, qName||localName);
67074                     var len = attrs.length;
67075                     appendElement(this, el);
67076                     this.currentElement = el;
67077                     
67078                         this.locator && position(this.locator,el);
67079                     for (var i = 0 ; i < len; i++) {
67080                         var namespaceURI = attrs.getURI(i);
67081                         var value = attrs.getValue(i);
67082                         var qName = attrs.getQName(i);
67083                                 var attr = doc.createAttributeNS(namespaceURI, qName);
67084                                 this.locator &&position(attrs.getLocator(i),attr);
67085                                 attr.value = attr.nodeValue = value;
67086                                 el.setAttributeNode(attr);
67087                     }
67088                 },
67089                 endElement:function(namespaceURI, localName, qName) {
67090                         var current = this.currentElement;
67091                         var tagName = current.tagName;
67092                         this.currentElement = current.parentNode;
67093                 },
67094                 startPrefixMapping:function(prefix, uri) {
67095                 },
67096                 endPrefixMapping:function(prefix) {
67097                 },
67098                 processingInstruction:function(target, data) {
67099                     var ins = this.doc.createProcessingInstruction(target, data);
67100                     this.locator && position(this.locator,ins);
67101                     appendElement(this, ins);
67102                 },
67103                 ignorableWhitespace:function(ch, start, length) {
67104                 },
67105                 characters:function(chars, start, length) {
67106                         chars = _toString.apply(this,arguments);
67107                         //console.log(chars)
67108                         if(chars){
67109                                 if (this.cdata) {
67110                                         var charNode = this.doc.createCDATASection(chars);
67111                                 } else {
67112                                         var charNode = this.doc.createTextNode(chars);
67113                                 }
67114                                 if(this.currentElement){
67115                                         this.currentElement.appendChild(charNode);
67116                                 }else if(/^\s*$/.test(chars)){
67117                                         this.doc.appendChild(charNode);
67118                                         //process xml
67119                                 }
67120                                 this.locator && position(this.locator,charNode);
67121                         }
67122                 },
67123                 skippedEntity:function(name) {
67124                 },
67125                 endDocument:function() {
67126                         this.doc.normalize();
67127                 },
67128                 setDocumentLocator:function (locator) {
67129                     if(this.locator = locator){// && !('lineNumber' in locator)){
67130                         locator.lineNumber = 0;
67131                     }
67132                 },
67133                 //LexicalHandler
67134                 comment:function(chars, start, length) {
67135                         chars = _toString.apply(this,arguments);
67136                     var comm = this.doc.createComment(chars);
67137                     this.locator && position(this.locator,comm);
67138                     appendElement(this, comm);
67139                 },
67140                 
67141                 startCDATA:function() {
67142                     //used in characters() methods
67143                     this.cdata = true;
67144                 },
67145                 endCDATA:function() {
67146                     this.cdata = false;
67147                 },
67148                 
67149                 startDTD:function(name, publicId, systemId) {
67150                         var impl = this.doc.implementation;
67151                     if (impl && impl.createDocumentType) {
67152                         var dt = impl.createDocumentType(name, publicId, systemId);
67153                         this.locator && position(this.locator,dt);
67154                         appendElement(this, dt);
67155                     }
67156                 },
67157                 /**
67158                  * @see org.xml.sax.ErrorHandler
67159                  * @link http://www.saxproject.org/apidoc/org/xml/sax/ErrorHandler.html
67160                  */
67161                 warning:function(error) {
67162                         console.warn('[xmldom warning]\t'+error,_locator(this.locator));
67163                 },
67164                 error:function(error) {
67165                         console.error('[xmldom error]\t'+error,_locator(this.locator));
67166                 },
67167                 fatalError:function(error) {
67168                         console.error('[xmldom fatalError]\t'+error,_locator(this.locator));
67169                     throw error;
67170                 }
67171         };
67172         function _locator(l){
67173                 if(l){
67174                         return '\n@'+(l.systemId ||'')+'#[line:'+l.lineNumber+',col:'+l.columnNumber+']'
67175                 }
67176         }
67177         function _toString(chars,start,length){
67178                 if(typeof chars == 'string'){
67179                         return chars.substr(start,length)
67180                 }else {//java sax connect width xmldom on rhino(what about: "? && !(chars instanceof String)")
67181                         if(chars.length >= start+length || start){
67182                                 return new java.lang.String(chars,start,length)+'';
67183                         }
67184                         return chars;
67185                 }
67186         }
67187
67188         /*
67189          * @link http://www.saxproject.org/apidoc/org/xml/sax/ext/LexicalHandler.html
67190          * used method of org.xml.sax.ext.LexicalHandler:
67191          *  #comment(chars, start, length)
67192          *  #startCDATA()
67193          *  #endCDATA()
67194          *  #startDTD(name, publicId, systemId)
67195          *
67196          *
67197          * IGNORED method of org.xml.sax.ext.LexicalHandler:
67198          *  #endDTD()
67199          *  #startEntity(name)
67200          *  #endEntity(name)
67201          *
67202          *
67203          * @link http://www.saxproject.org/apidoc/org/xml/sax/ext/DeclHandler.html
67204          * IGNORED method of org.xml.sax.ext.DeclHandler
67205          *      #attributeDecl(eName, aName, type, mode, value)
67206          *  #elementDecl(name, model)
67207          *  #externalEntityDecl(name, publicId, systemId)
67208          *  #internalEntityDecl(name, value)
67209          * @link http://www.saxproject.org/apidoc/org/xml/sax/ext/EntityResolver2.html
67210          * IGNORED method of org.xml.sax.EntityResolver2
67211          *  #resolveEntity(String name,String publicId,String baseURI,String systemId)
67212          *  #resolveEntity(publicId, systemId)
67213          *  #getExternalSubset(name, baseURI)
67214          * @link http://www.saxproject.org/apidoc/org/xml/sax/DTDHandler.html
67215          * IGNORED method of org.xml.sax.DTDHandler
67216          *  #notationDecl(name, publicId, systemId) {};
67217          *  #unparsedEntityDecl(name, publicId, systemId, notationName) {};
67218          */
67219         "endDTD,startEntity,endEntity,attributeDecl,elementDecl,externalEntityDecl,internalEntityDecl,resolveEntity,getExternalSubset,notationDecl,unparsedEntityDecl".replace(/\w+/g,function(key){
67220                 DOMHandler.prototype[key] = function(){return null};
67221         });
67222
67223         /* 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 */
67224         function appendElement (hander,node) {
67225             if (!hander.currentElement) {
67226                 hander.doc.appendChild(node);
67227             } else {
67228                 hander.currentElement.appendChild(node);
67229             }
67230         }//appendChild and setAttributeNS are preformance key
67231
67232         //if(typeof require == 'function'){
67233                 var XMLReader = sax.XMLReader;
67234                 var DOMImplementation = exports.DOMImplementation = dom.DOMImplementation;
67235                 exports.XMLSerializer = dom.XMLSerializer ;
67236                 exports.DOMParser = DOMParser;
67237         //}
67238         });
67239
67240         var togeojson = createCommonjsModule(function (module, exports) {
67241         var toGeoJSON = (function() {
67242
67243             var removeSpace = /\s*/g,
67244                 trimSpace = /^\s*|\s*$/g,
67245                 splitSpace = /\s+/;
67246             // generate a short, numeric hash of a string
67247             function okhash(x) {
67248                 if (!x || !x.length) { return 0; }
67249                 for (var i = 0, h = 0; i < x.length; i++) {
67250                     h = ((h << 5) - h) + x.charCodeAt(i) | 0;
67251                 } return h;
67252             }
67253             // all Y children of X
67254             function get(x, y) { return x.getElementsByTagName(y); }
67255             function attr(x, y) { return x.getAttribute(y); }
67256             function attrf(x, y) { return parseFloat(attr(x, y)); }
67257             // one Y child of X, if any, otherwise null
67258             function get1(x, y) { var n = get(x, y); return n.length ? n[0] : null; }
67259             // https://developer.mozilla.org/en-US/docs/Web/API/Node.normalize
67260             function norm(el) { if (el.normalize) { el.normalize(); } return el; }
67261             // cast array x into numbers
67262             function numarray(x) {
67263                 for (var j = 0, o = []; j < x.length; j++) { o[j] = parseFloat(x[j]); }
67264                 return o;
67265             }
67266             // get the content of a text node, if any
67267             function nodeVal(x) {
67268                 if (x) { norm(x); }
67269                 return (x && x.textContent) || '';
67270             }
67271             // get the contents of multiple text nodes, if present
67272             function getMulti(x, ys) {
67273                 var o = {}, n, k;
67274                 for (k = 0; k < ys.length; k++) {
67275                     n = get1(x, ys[k]);
67276                     if (n) { o[ys[k]] = nodeVal(n); }
67277                 }
67278                 return o;
67279             }
67280             // add properties of Y to X, overwriting if present in both
67281             function extend(x, y) { for (var k in y) { x[k] = y[k]; } }
67282             // get one coordinate from a coordinate array, if any
67283             function coord1(v) { return numarray(v.replace(removeSpace, '').split(',')); }
67284             // get all coordinates from a coordinate array as [[],[]]
67285             function coord(v) {
67286                 var coords = v.replace(trimSpace, '').split(splitSpace),
67287                     o = [];
67288                 for (var i = 0; i < coords.length; i++) {
67289                     o.push(coord1(coords[i]));
67290                 }
67291                 return o;
67292             }
67293             function coordPair(x) {
67294                 var ll = [attrf(x, 'lon'), attrf(x, 'lat')],
67295                     ele = get1(x, 'ele'),
67296                     // handle namespaced attribute in browser
67297                     heartRate = get1(x, 'gpxtpx:hr') || get1(x, 'hr'),
67298                     time = get1(x, 'time'),
67299                     e;
67300                 if (ele) {
67301                     e = parseFloat(nodeVal(ele));
67302                     if (!isNaN(e)) {
67303                         ll.push(e);
67304                     }
67305                 }
67306                 return {
67307                     coordinates: ll,
67308                     time: time ? nodeVal(time) : null,
67309                     heartRate: heartRate ? parseFloat(nodeVal(heartRate)) : null
67310                 };
67311             }
67312
67313             // create a new feature collection parent object
67314             function fc() {
67315                 return {
67316                     type: 'FeatureCollection',
67317                     features: []
67318                 };
67319             }
67320
67321             var serializer;
67322             if (typeof XMLSerializer !== 'undefined') {
67323                 /* istanbul ignore next */
67324                 serializer = new XMLSerializer();
67325             // only require xmldom in a node environment
67326             } else if ( typeof process === 'object' && !process.browser) {
67327                 serializer = new (domParser.XMLSerializer)();
67328             }
67329             function xml2str(str) {
67330                 // IE9 will create a new XMLSerializer but it'll crash immediately.
67331                 // This line is ignored because we don't run coverage tests in IE9
67332                 /* istanbul ignore next */
67333                 if (str.xml !== undefined) { return str.xml; }
67334                 return serializer.serializeToString(str);
67335             }
67336
67337             var t = {
67338                 kml: function(doc) {
67339
67340                     var gj = fc(),
67341                         // styleindex keeps track of hashed styles in order to match features
67342                         styleIndex = {}, styleByHash = {},
67343                         // stylemapindex keeps track of style maps to expose in properties
67344                         styleMapIndex = {},
67345                         // atomic geospatial types supported by KML - MultiGeometry is
67346                         // handled separately
67347                         geotypes = ['Polygon', 'LineString', 'Point', 'Track', 'gx:Track'],
67348                         // all root placemarks in the file
67349                         placemarks = get(doc, 'Placemark'),
67350                         styles = get(doc, 'Style'),
67351                         styleMaps = get(doc, 'StyleMap');
67352
67353                     for (var k = 0; k < styles.length; k++) {
67354                         var hash = okhash(xml2str(styles[k])).toString(16);
67355                         styleIndex['#' + attr(styles[k], 'id')] = hash;
67356                         styleByHash[hash] = styles[k];
67357                     }
67358                     for (var l = 0; l < styleMaps.length; l++) {
67359                         styleIndex['#' + attr(styleMaps[l], 'id')] = okhash(xml2str(styleMaps[l])).toString(16);
67360                         var pairs = get(styleMaps[l], 'Pair');
67361                         var pairsMap = {};
67362                         for (var m = 0; m < pairs.length; m++) {
67363                             pairsMap[nodeVal(get1(pairs[m], 'key'))] = nodeVal(get1(pairs[m], 'styleUrl'));
67364                         }
67365                         styleMapIndex['#' + attr(styleMaps[l], 'id')] = pairsMap;
67366
67367                     }
67368                     for (var j = 0; j < placemarks.length; j++) {
67369                         gj.features = gj.features.concat(getPlacemark(placemarks[j]));
67370                     }
67371                     function kmlColor(v) {
67372                         var color, opacity;
67373                         v = v || '';
67374                         if (v.substr(0, 1) === '#') { v = v.substr(1); }
67375                         if (v.length === 6 || v.length === 3) { color = v; }
67376                         if (v.length === 8) {
67377                             opacity = parseInt(v.substr(0, 2), 16) / 255;
67378                             color = '#' + v.substr(6, 2) +
67379                                 v.substr(4, 2) +
67380                                 v.substr(2, 2);
67381                         }
67382                         return [color, isNaN(opacity) ? undefined : opacity];
67383                     }
67384                     function gxCoord(v) { return numarray(v.split(' ')); }
67385                     function gxCoords(root) {
67386                         var elems = get(root, 'coord'), coords = [], times = [];
67387                         if (elems.length === 0) { elems = get(root, 'gx:coord'); }
67388                         for (var i = 0; i < elems.length; i++) { coords.push(gxCoord(nodeVal(elems[i]))); }
67389                         var timeElems = get(root, 'when');
67390                         for (var j = 0; j < timeElems.length; j++) { times.push(nodeVal(timeElems[j])); }
67391                         return {
67392                             coords: coords,
67393                             times: times
67394                         };
67395                     }
67396                     function getGeometry(root) {
67397                         var geomNode, geomNodes, i, j, k, geoms = [], coordTimes = [];
67398                         if (get1(root, 'MultiGeometry')) { return getGeometry(get1(root, 'MultiGeometry')); }
67399                         if (get1(root, 'MultiTrack')) { return getGeometry(get1(root, 'MultiTrack')); }
67400                         if (get1(root, 'gx:MultiTrack')) { return getGeometry(get1(root, 'gx:MultiTrack')); }
67401                         for (i = 0; i < geotypes.length; i++) {
67402                             geomNodes = get(root, geotypes[i]);
67403                             if (geomNodes) {
67404                                 for (j = 0; j < geomNodes.length; j++) {
67405                                     geomNode = geomNodes[j];
67406                                     if (geotypes[i] === 'Point') {
67407                                         geoms.push({
67408                                             type: 'Point',
67409                                             coordinates: coord1(nodeVal(get1(geomNode, 'coordinates')))
67410                                         });
67411                                     } else if (geotypes[i] === 'LineString') {
67412                                         geoms.push({
67413                                             type: 'LineString',
67414                                             coordinates: coord(nodeVal(get1(geomNode, 'coordinates')))
67415                                         });
67416                                     } else if (geotypes[i] === 'Polygon') {
67417                                         var rings = get(geomNode, 'LinearRing'),
67418                                             coords = [];
67419                                         for (k = 0; k < rings.length; k++) {
67420                                             coords.push(coord(nodeVal(get1(rings[k], 'coordinates'))));
67421                                         }
67422                                         geoms.push({
67423                                             type: 'Polygon',
67424                                             coordinates: coords
67425                                         });
67426                                     } else if (geotypes[i] === 'Track' ||
67427                                         geotypes[i] === 'gx:Track') {
67428                                         var track = gxCoords(geomNode);
67429                                         geoms.push({
67430                                             type: 'LineString',
67431                                             coordinates: track.coords
67432                                         });
67433                                         if (track.times.length) { coordTimes.push(track.times); }
67434                                     }
67435                                 }
67436                             }
67437                         }
67438                         return {
67439                             geoms: geoms,
67440                             coordTimes: coordTimes
67441                         };
67442                     }
67443                     function getPlacemark(root) {
67444                         var geomsAndTimes = getGeometry(root), i, properties = {},
67445                             name = nodeVal(get1(root, 'name')),
67446                             address = nodeVal(get1(root, 'address')),
67447                             styleUrl = nodeVal(get1(root, 'styleUrl')),
67448                             description = nodeVal(get1(root, 'description')),
67449                             timeSpan = get1(root, 'TimeSpan'),
67450                             timeStamp = get1(root, 'TimeStamp'),
67451                             extendedData = get1(root, 'ExtendedData'),
67452                             lineStyle = get1(root, 'LineStyle'),
67453                             polyStyle = get1(root, 'PolyStyle'),
67454                             visibility = get1(root, 'visibility');
67455
67456                         if (!geomsAndTimes.geoms.length) { return []; }
67457                         if (name) { properties.name = name; }
67458                         if (address) { properties.address = address; }
67459                         if (styleUrl) {
67460                             if (styleUrl[0] !== '#') {
67461                                 styleUrl = '#' + styleUrl;
67462                             }
67463
67464                             properties.styleUrl = styleUrl;
67465                             if (styleIndex[styleUrl]) {
67466                                 properties.styleHash = styleIndex[styleUrl];
67467                             }
67468                             if (styleMapIndex[styleUrl]) {
67469                                 properties.styleMapHash = styleMapIndex[styleUrl];
67470                                 properties.styleHash = styleIndex[styleMapIndex[styleUrl].normal];
67471                             }
67472                             // Try to populate the lineStyle or polyStyle since we got the style hash
67473                             var style = styleByHash[properties.styleHash];
67474                             if (style) {
67475                                 if (!lineStyle) { lineStyle = get1(style, 'LineStyle'); }
67476                                 if (!polyStyle) { polyStyle = get1(style, 'PolyStyle'); }
67477                             }
67478                         }
67479                         if (description) { properties.description = description; }
67480                         if (timeSpan) {
67481                             var begin = nodeVal(get1(timeSpan, 'begin'));
67482                             var end = nodeVal(get1(timeSpan, 'end'));
67483                             properties.timespan = { begin: begin, end: end };
67484                         }
67485                         if (timeStamp) {
67486                             properties.timestamp = nodeVal(get1(timeStamp, 'when'));
67487                         }
67488                         if (lineStyle) {
67489                             var linestyles = kmlColor(nodeVal(get1(lineStyle, 'color'))),
67490                                 color = linestyles[0],
67491                                 opacity = linestyles[1],
67492                                 width = parseFloat(nodeVal(get1(lineStyle, 'width')));
67493                             if (color) { properties.stroke = color; }
67494                             if (!isNaN(opacity)) { properties['stroke-opacity'] = opacity; }
67495                             if (!isNaN(width)) { properties['stroke-width'] = width; }
67496                         }
67497                         if (polyStyle) {
67498                             var polystyles = kmlColor(nodeVal(get1(polyStyle, 'color'))),
67499                                 pcolor = polystyles[0],
67500                                 popacity = polystyles[1],
67501                                 fill = nodeVal(get1(polyStyle, 'fill')),
67502                                 outline = nodeVal(get1(polyStyle, 'outline'));
67503                             if (pcolor) { properties.fill = pcolor; }
67504                             if (!isNaN(popacity)) { properties['fill-opacity'] = popacity; }
67505                             if (fill) { properties['fill-opacity'] = fill === '1' ? properties['fill-opacity'] || 1 : 0; }
67506                             if (outline) { properties['stroke-opacity'] = outline === '1' ? properties['stroke-opacity'] || 1 : 0; }
67507                         }
67508                         if (extendedData) {
67509                             var datas = get(extendedData, 'Data'),
67510                                 simpleDatas = get(extendedData, 'SimpleData');
67511
67512                             for (i = 0; i < datas.length; i++) {
67513                                 properties[datas[i].getAttribute('name')] = nodeVal(get1(datas[i], 'value'));
67514                             }
67515                             for (i = 0; i < simpleDatas.length; i++) {
67516                                 properties[simpleDatas[i].getAttribute('name')] = nodeVal(simpleDatas[i]);
67517                             }
67518                         }
67519                         if (visibility) {
67520                             properties.visibility = nodeVal(visibility);
67521                         }
67522                         if (geomsAndTimes.coordTimes.length) {
67523                             properties.coordTimes = (geomsAndTimes.coordTimes.length === 1) ?
67524                                 geomsAndTimes.coordTimes[0] : geomsAndTimes.coordTimes;
67525                         }
67526                         var feature = {
67527                             type: 'Feature',
67528                             geometry: (geomsAndTimes.geoms.length === 1) ? geomsAndTimes.geoms[0] : {
67529                                 type: 'GeometryCollection',
67530                                 geometries: geomsAndTimes.geoms
67531                             },
67532                             properties: properties
67533                         };
67534                         if (attr(root, 'id')) { feature.id = attr(root, 'id'); }
67535                         return [feature];
67536                     }
67537                     return gj;
67538                 },
67539                 gpx: function(doc) {
67540                     var i,
67541                         tracks = get(doc, 'trk'),
67542                         routes = get(doc, 'rte'),
67543                         waypoints = get(doc, 'wpt'),
67544                         // a feature collection
67545                         gj = fc(),
67546                         feature;
67547                     for (i = 0; i < tracks.length; i++) {
67548                         feature = getTrack(tracks[i]);
67549                         if (feature) { gj.features.push(feature); }
67550                     }
67551                     for (i = 0; i < routes.length; i++) {
67552                         feature = getRoute(routes[i]);
67553                         if (feature) { gj.features.push(feature); }
67554                     }
67555                     for (i = 0; i < waypoints.length; i++) {
67556                         gj.features.push(getPoint(waypoints[i]));
67557                     }
67558                     function getPoints(node, pointname) {
67559                         var pts = get(node, pointname),
67560                             line = [],
67561                             times = [],
67562                             heartRates = [],
67563                             l = pts.length;
67564                         if (l < 2) { return {}; }  // Invalid line in GeoJSON
67565                         for (var i = 0; i < l; i++) {
67566                             var c = coordPair(pts[i]);
67567                             line.push(c.coordinates);
67568                             if (c.time) { times.push(c.time); }
67569                             if (c.heartRate) { heartRates.push(c.heartRate); }
67570                         }
67571                         return {
67572                             line: line,
67573                             times: times,
67574                             heartRates: heartRates
67575                         };
67576                     }
67577                     function getTrack(node) {
67578                         var segments = get(node, 'trkseg'),
67579                             track = [],
67580                             times = [],
67581                             heartRates = [],
67582                             line;
67583                         for (var i = 0; i < segments.length; i++) {
67584                             line = getPoints(segments[i], 'trkpt');
67585                             if (line) {
67586                                 if (line.line) { track.push(line.line); }
67587                                 if (line.times && line.times.length) { times.push(line.times); }
67588                                 if (line.heartRates && line.heartRates.length) { heartRates.push(line.heartRates); }
67589                             }
67590                         }
67591                         if (track.length === 0) { return; }
67592                         var properties = getProperties(node);
67593                         extend(properties, getLineStyle(get1(node, 'extensions')));
67594                         if (times.length) { properties.coordTimes = track.length === 1 ? times[0] : times; }
67595                         if (heartRates.length) { properties.heartRates = track.length === 1 ? heartRates[0] : heartRates; }
67596                         return {
67597                             type: 'Feature',
67598                             properties: properties,
67599                             geometry: {
67600                                 type: track.length === 1 ? 'LineString' : 'MultiLineString',
67601                                 coordinates: track.length === 1 ? track[0] : track
67602                             }
67603                         };
67604                     }
67605                     function getRoute(node) {
67606                         var line = getPoints(node, 'rtept');
67607                         if (!line.line) { return; }
67608                         var prop = getProperties(node);
67609                         extend(prop, getLineStyle(get1(node, 'extensions')));
67610                         var routeObj = {
67611                             type: 'Feature',
67612                             properties: prop,
67613                             geometry: {
67614                                 type: 'LineString',
67615                                 coordinates: line.line
67616                             }
67617                         };
67618                         return routeObj;
67619                     }
67620                     function getPoint(node) {
67621                         var prop = getProperties(node);
67622                         extend(prop, getMulti(node, ['sym']));
67623                         return {
67624                             type: 'Feature',
67625                             properties: prop,
67626                             geometry: {
67627                                 type: 'Point',
67628                                 coordinates: coordPair(node).coordinates
67629                             }
67630                         };
67631                     }
67632                     function getLineStyle(extensions) {
67633                         var style = {};
67634                         if (extensions) {
67635                             var lineStyle = get1(extensions, 'line');
67636                             if (lineStyle) {
67637                                 var color = nodeVal(get1(lineStyle, 'color')),
67638                                     opacity = parseFloat(nodeVal(get1(lineStyle, 'opacity'))),
67639                                     width = parseFloat(nodeVal(get1(lineStyle, 'width')));
67640                                 if (color) { style.stroke = color; }
67641                                 if (!isNaN(opacity)) { style['stroke-opacity'] = opacity; }
67642                                 // GPX width is in mm, convert to px with 96 px per inch
67643                                 if (!isNaN(width)) { style['stroke-width'] = width * 96 / 25.4; }
67644                             }
67645                         }
67646                         return style;
67647                     }
67648                     function getProperties(node) {
67649                         var prop = getMulti(node, ['name', 'cmt', 'desc', 'type', 'time', 'keywords']),
67650                             links = get(node, 'link');
67651                         if (links.length) { prop.links = []; }
67652                         for (var i = 0, link; i < links.length; i++) {
67653                             link = { href: attr(links[i], 'href') };
67654                             extend(link, getMulti(links[i], ['text', 'type']));
67655                             prop.links.push(link);
67656                         }
67657                         return prop;
67658                     }
67659                     return gj;
67660                 }
67661             };
67662             return t;
67663         })();
67664
67665         { module.exports = toGeoJSON; }
67666         });
67667
67668         var _initialized = false;
67669         var _enabled = false;
67670         var _geojson;
67671
67672
67673         function svgData(projection, context, dispatch) {
67674             var throttledRedraw = throttle(function () { dispatch.call('change'); }, 1000);
67675             var _showLabels = true;
67676             var detected = utilDetect();
67677             var layer = select(null);
67678             var _vtService;
67679             var _fileList;
67680             var _template;
67681             var _src;
67682
67683
67684             function init() {
67685                 if (_initialized) { return; }  // run once
67686
67687                 _geojson = {};
67688                 _enabled = true;
67689
67690                 function over() {
67691                     event.stopPropagation();
67692                     event.preventDefault();
67693                     event.dataTransfer.dropEffect = 'copy';
67694                 }
67695
67696                 context.container()
67697                     .attr('dropzone', 'copy')
67698                     .on('drop.svgData', function() {
67699                         event.stopPropagation();
67700                         event.preventDefault();
67701                         if (!detected.filedrop) { return; }
67702                         drawData.fileList(event.dataTransfer.files);
67703                     })
67704                     .on('dragenter.svgData', over)
67705                     .on('dragexit.svgData', over)
67706                     .on('dragover.svgData', over);
67707
67708                 _initialized = true;
67709             }
67710
67711
67712             function getService() {
67713                 if (services.vectorTile && !_vtService) {
67714                     _vtService = services.vectorTile;
67715                     _vtService.event.on('loadedData', throttledRedraw);
67716                 } else if (!services.vectorTile && _vtService) {
67717                     _vtService = null;
67718                 }
67719
67720                 return _vtService;
67721             }
67722
67723
67724             function showLayer() {
67725                 layerOn();
67726
67727                 layer
67728                     .style('opacity', 0)
67729                     .transition()
67730                     .duration(250)
67731                     .style('opacity', 1)
67732                     .on('end', function () { dispatch.call('change'); });
67733             }
67734
67735
67736             function hideLayer() {
67737                 throttledRedraw.cancel();
67738
67739                 layer
67740                     .transition()
67741                     .duration(250)
67742                     .style('opacity', 0)
67743                     .on('end', layerOff);
67744             }
67745
67746
67747             function layerOn() {
67748                 layer.style('display', 'block');
67749             }
67750
67751
67752             function layerOff() {
67753                 layer.selectAll('.viewfield-group').remove();
67754                 layer.style('display', 'none');
67755             }
67756
67757
67758             // ensure that all geojson features in a collection have IDs
67759             function ensureIDs(gj) {
67760                 if (!gj) { return null; }
67761
67762                 if (gj.type === 'FeatureCollection') {
67763                     for (var i = 0; i < gj.features.length; i++) {
67764                         ensureFeatureID(gj.features[i]);
67765                     }
67766                 } else {
67767                     ensureFeatureID(gj);
67768                 }
67769                 return gj;
67770             }
67771
67772
67773             // ensure that each single Feature object has a unique ID
67774             function ensureFeatureID(feature) {
67775                 if (!feature) { return; }
67776                 feature.__featurehash__ = utilHashcode(fastJsonStableStringify(feature));
67777                 return feature;
67778             }
67779
67780
67781             // Prefer an array of Features instead of a FeatureCollection
67782             function getFeatures(gj) {
67783                 if (!gj) { return []; }
67784
67785                 if (gj.type === 'FeatureCollection') {
67786                     return gj.features;
67787                 } else {
67788                     return [gj];
67789                 }
67790             }
67791
67792
67793             function featureKey(d) {
67794                 return d.__featurehash__;
67795             }
67796
67797
67798             function isPolygon(d) {
67799                 return d.geometry.type === 'Polygon' || d.geometry.type === 'MultiPolygon';
67800             }
67801
67802
67803             function clipPathID(d) {
67804                 return 'ideditor-data-' + d.__featurehash__ + '-clippath';
67805             }
67806
67807
67808             function featureClasses(d) {
67809                 return [
67810                     'data' + d.__featurehash__,
67811                     d.geometry.type,
67812                     isPolygon(d) ? 'area' : '',
67813                     d.__layerID__ || ''
67814                 ].filter(Boolean).join(' ');
67815             }
67816
67817
67818             function drawData(selection) {
67819                 var vtService = getService();
67820                 var getPath = svgPath(projection).geojson;
67821                 var getAreaPath = svgPath(projection, null, true).geojson;
67822                 var hasData = drawData.hasData();
67823
67824                 layer = selection.selectAll('.layer-mapdata')
67825                     .data(_enabled && hasData ? [0] : []);
67826
67827                 layer.exit()
67828                     .remove();
67829
67830                 layer = layer.enter()
67831                     .append('g')
67832                     .attr('class', 'layer-mapdata')
67833                     .merge(layer);
67834
67835                 var surface = context.surface();
67836                 if (!surface || surface.empty()) { return; }  // not ready to draw yet, starting up
67837
67838
67839                 // Gather data
67840                 var geoData, polygonData;
67841                 if (_template && vtService) {   // fetch data from vector tile service
67842                     var sourceID = _template;
67843                     vtService.loadTiles(sourceID, _template, projection);
67844                     geoData = vtService.data(sourceID, projection);
67845                 } else {
67846                     geoData = getFeatures(_geojson);
67847                 }
67848                 geoData = geoData.filter(getPath);
67849                 polygonData = geoData.filter(isPolygon);
67850
67851
67852                 // Draw clip paths for polygons
67853                 var clipPaths = surface.selectAll('defs').selectAll('.clipPath-data')
67854                    .data(polygonData, featureKey);
67855
67856                 clipPaths.exit()
67857                    .remove();
67858
67859                 var clipPathsEnter = clipPaths.enter()
67860                    .append('clipPath')
67861                    .attr('class', 'clipPath-data')
67862                    .attr('id', clipPathID);
67863
67864                 clipPathsEnter
67865                    .append('path');
67866
67867                 clipPaths.merge(clipPathsEnter)
67868                    .selectAll('path')
67869                    .attr('d', getAreaPath);
67870
67871
67872                 // Draw fill, shadow, stroke layers
67873                 var datagroups = layer
67874                     .selectAll('g.datagroup')
67875                     .data(['fill', 'shadow', 'stroke']);
67876
67877                 datagroups = datagroups.enter()
67878                     .append('g')
67879                     .attr('class', function(d) { return 'datagroup datagroup-' + d; })
67880                     .merge(datagroups);
67881
67882
67883                 // Draw paths
67884                 var pathData = {
67885                     fill: polygonData,
67886                     shadow: geoData,
67887                     stroke: geoData
67888                 };
67889
67890                 var paths = datagroups
67891                     .selectAll('path')
67892                     .data(function(layer) { return pathData[layer]; }, featureKey);
67893
67894                 // exit
67895                 paths.exit()
67896                     .remove();
67897
67898                 // enter/update
67899                 paths = paths.enter()
67900                     .append('path')
67901                     .attr('class', function(d) {
67902                         var datagroup = this.parentNode.__data__;
67903                         return 'pathdata ' + datagroup + ' ' + featureClasses(d);
67904                     })
67905                     .attr('clip-path', function(d) {
67906                         var datagroup = this.parentNode.__data__;
67907                         return datagroup === 'fill' ? ('url(#' + clipPathID(d) + ')') : null;
67908                     })
67909                     .merge(paths)
67910                     .attr('d', function(d) {
67911                         var datagroup = this.parentNode.__data__;
67912                         return datagroup === 'fill' ? getAreaPath(d) : getPath(d);
67913                     });
67914
67915
67916                 // Draw labels
67917                 layer
67918                     .call(drawLabels, 'label-halo', geoData)
67919                     .call(drawLabels, 'label', geoData);
67920
67921
67922                 function drawLabels(selection, textClass, data) {
67923                     var labelPath = d3_geoPath(projection);
67924                     var labelData = data.filter(function(d) {
67925                         return _showLabels && d.properties && (d.properties.desc || d.properties.name);
67926                     });
67927
67928                     var labels = selection.selectAll('text.' + textClass)
67929                         .data(labelData, featureKey);
67930
67931                     // exit
67932                     labels.exit()
67933                         .remove();
67934
67935                     // enter/update
67936                     labels = labels.enter()
67937                         .append('text')
67938                         .attr('class', function(d) { return textClass + ' ' + featureClasses(d); })
67939                         .merge(labels)
67940                         .text(function(d) {
67941                             return d.properties.desc || d.properties.name;
67942                         })
67943                         .attr('x', function(d) {
67944                             var centroid = labelPath.centroid(d);
67945                             return centroid[0] + 11;
67946                         })
67947                         .attr('y', function(d) {
67948                             var centroid = labelPath.centroid(d);
67949                             return centroid[1];
67950                         });
67951                 }
67952             }
67953
67954
67955             function getExtension(fileName) {
67956                 if (!fileName) { return; }
67957
67958                 var re = /\.(gpx|kml|(geo)?json)$/i;
67959                 var match = fileName.toLowerCase().match(re);
67960                 return match && match.length && match[0];
67961             }
67962
67963
67964             function xmlToDom(textdata) {
67965                 return (new DOMParser()).parseFromString(textdata, 'text/xml');
67966             }
67967
67968
67969             drawData.setFile = function(extension, data) {
67970                 _template = null;
67971                 _fileList = null;
67972                 _geojson = null;
67973                 _src = null;
67974
67975                 var gj;
67976                 switch (extension) {
67977                     case '.gpx':
67978                         gj = togeojson.gpx(xmlToDom(data));
67979                         break;
67980                     case '.kml':
67981                         gj = togeojson.kml(xmlToDom(data));
67982                         break;
67983                     case '.geojson':
67984                     case '.json':
67985                         gj = JSON.parse(data);
67986                         break;
67987                 }
67988
67989                 gj = gj || {};
67990                 if (Object.keys(gj).length) {
67991                     _geojson = ensureIDs(gj);
67992                     _src = extension + ' data file';
67993                     this.fitZoom();
67994                 }
67995
67996                 dispatch.call('change');
67997                 return this;
67998             };
67999
68000
68001             drawData.showLabels = function(val) {
68002                 if (!arguments.length) { return _showLabels; }
68003
68004                 _showLabels = val;
68005                 return this;
68006             };
68007
68008
68009             drawData.enabled = function(val) {
68010                 if (!arguments.length) { return _enabled; }
68011
68012                 _enabled = val;
68013                 if (_enabled) {
68014                     showLayer();
68015                 } else {
68016                     hideLayer();
68017                 }
68018
68019                 dispatch.call('change');
68020                 return this;
68021             };
68022
68023
68024             drawData.hasData = function() {
68025                 var gj = _geojson || {};
68026                 return !!(_template || Object.keys(gj).length);
68027             };
68028
68029
68030             drawData.template = function(val, src) {
68031                 if (!arguments.length) { return _template; }
68032
68033                 // test source against OSM imagery blocklists..
68034                 var osm = context.connection();
68035                 if (osm) {
68036                     var blocklists = osm.imageryBlocklists();
68037                     var fail = false;
68038                     var tested = 0;
68039                     var regex;
68040
68041                     for (var i = 0; i < blocklists.length; i++) {
68042                         regex = blocklists[i];
68043                         fail = regex.test(val);
68044                         tested++;
68045                         if (fail) { break; }
68046                     }
68047
68048                     // ensure at least one test was run.
68049                     if (!tested) {
68050                         regex = /.*\.google(apis)?\..*\/(vt|kh)[\?\/].*([xyz]=.*){3}.*/;
68051                         fail = regex.test(val);
68052                     }
68053                 }
68054
68055                 _template = val;
68056                 _fileList = null;
68057                 _geojson = null;
68058
68059                 // strip off the querystring/hash from the template,
68060                 // it often includes the access token
68061                 _src = src || ('vectortile:' + val.split(/[?#]/)[0]);
68062
68063                 dispatch.call('change');
68064                 return this;
68065             };
68066
68067
68068             drawData.geojson = function(gj, src) {
68069                 if (!arguments.length) { return _geojson; }
68070
68071                 _template = null;
68072                 _fileList = null;
68073                 _geojson = null;
68074                 _src = null;
68075
68076                 gj = gj || {};
68077                 if (Object.keys(gj).length) {
68078                     _geojson = ensureIDs(gj);
68079                     _src = src || 'unknown.geojson';
68080                 }
68081
68082                 dispatch.call('change');
68083                 return this;
68084             };
68085
68086
68087             drawData.fileList = function(fileList) {
68088                 if (!arguments.length) { return _fileList; }
68089
68090                 _template = null;
68091                 _fileList = fileList;
68092                 _geojson = null;
68093                 _src = null;
68094
68095                 if (!fileList || !fileList.length) { return this; }
68096                 var f = fileList[0];
68097                 var extension = getExtension(f.name);
68098                 var reader = new FileReader();
68099                 reader.onload = (function() {
68100                     return function(e) {
68101                         drawData.setFile(extension, e.target.result);
68102                     };
68103                 })();
68104
68105                 reader.readAsText(f);
68106
68107                 return this;
68108             };
68109
68110
68111             drawData.url = function(url, defaultExtension) {
68112                 _template = null;
68113                 _fileList = null;
68114                 _geojson = null;
68115                 _src = null;
68116
68117                 // strip off any querystring/hash from the url before checking extension
68118                 var testUrl = url.split(/[?#]/)[0];
68119                 var extension = getExtension(testUrl) || defaultExtension;
68120                 if (extension) {
68121                     _template = null;
68122                     d3_text(url)
68123                         .then(function(data) {
68124                             drawData.setFile(extension, data);
68125                         })
68126                         .catch(function() {
68127                             /* ignore */
68128                         });
68129
68130                 } else {
68131                     drawData.template(url);
68132                 }
68133
68134                 return this;
68135             };
68136
68137
68138             drawData.getSrc = function() {
68139                 return _src || '';
68140             };
68141
68142
68143             drawData.fitZoom = function() {
68144                 var features = getFeatures(_geojson);
68145                 if (!features.length) { return; }
68146
68147                 var map = context.map();
68148                 var viewport = map.trimmedExtent().polygon();
68149                 var coords = features.reduce(function(coords, feature) {
68150                     var geom = feature.geometry;
68151                     if (!geom) { return coords; }
68152
68153                     var c = geom.coordinates;
68154
68155                     /* eslint-disable no-fallthrough */
68156                     switch (geom.type) {
68157                         case 'Point':
68158                             c = [c];
68159                         case 'MultiPoint':
68160                         case 'LineString':
68161                             break;
68162
68163                         case 'MultiPolygon':
68164                             c = utilArrayFlatten(c);
68165                         case 'Polygon':
68166                         case 'MultiLineString':
68167                             c = utilArrayFlatten(c);
68168                             break;
68169                     }
68170                     /* eslint-enable no-fallthrough */
68171
68172                     return utilArrayUnion(coords, c);
68173                 }, []);
68174
68175                 if (!geoPolygonIntersectsPolygon(viewport, coords, true)) {
68176                     var extent = geoExtent(d3_geoBounds({ type: 'LineString', coordinates: coords }));
68177                     map.centerZoom(extent.center(), map.trimmedExtentZoom(extent));
68178                 }
68179
68180                 return this;
68181             };
68182
68183
68184             init();
68185             return drawData;
68186         }
68187
68188         function svgDebug(projection, context) {
68189
68190           function drawDebug(selection) {
68191             var showTile = context.getDebug('tile');
68192             var showCollision = context.getDebug('collision');
68193             var showImagery = context.getDebug('imagery');
68194             var showTouchTargets = context.getDebug('target');
68195             var showDownloaded = context.getDebug('downloaded');
68196
68197             var debugData = [];
68198             if (showTile) {
68199               debugData.push({ class: 'red', label: 'tile' });
68200             }
68201             if (showCollision) {
68202               debugData.push({ class: 'yellow', label: 'collision' });
68203             }
68204             if (showImagery) {
68205               debugData.push({ class: 'orange', label: 'imagery' });
68206             }
68207             if (showTouchTargets) {
68208               debugData.push({ class: 'pink', label: 'touchTargets' });
68209             }
68210             if (showDownloaded) {
68211               debugData.push({ class: 'purple', label: 'downloaded' });
68212             }
68213
68214
68215             var legend = context.container().select('.main-content')
68216               .selectAll('.debug-legend')
68217               .data(debugData.length ? [0] : []);
68218
68219             legend.exit()
68220               .remove();
68221
68222             legend = legend.enter()
68223               .append('div')
68224               .attr('class', 'fillD debug-legend')
68225               .merge(legend);
68226
68227
68228             var legendItems = legend.selectAll('.debug-legend-item')
68229               .data(debugData, function (d) { return d.label; });
68230
68231             legendItems.exit()
68232               .remove();
68233
68234             legendItems.enter()
68235               .append('span')
68236               .attr('class', function (d) { return ("debug-legend-item " + (d.class)); })
68237               .text(function (d) { return d.label; });
68238
68239
68240             var layer = selection.selectAll('.layer-debug')
68241               .data(showImagery || showDownloaded ? [0] : []);
68242
68243             layer.exit()
68244               .remove();
68245
68246             layer = layer.enter()
68247               .append('g')
68248               .attr('class', 'layer-debug')
68249               .merge(layer);
68250
68251
68252             // imagery
68253             var extent = context.map().extent();
68254             _mainFileFetcher.get('imagery')
68255               .then(function (d) {
68256                 var hits = (showImagery && d.query.bbox(extent.rectangle(), true)) || [];
68257                 var features = hits.map(function (d) { return d.features[d.id]; });
68258
68259                 var imagery = layer.selectAll('path.debug-imagery')
68260                   .data(features);
68261
68262                 imagery.exit()
68263                   .remove();
68264
68265                 imagery.enter()
68266                   .append('path')
68267                   .attr('class', 'debug-imagery debug orange');
68268               })
68269               .catch(function () { /* ignore */ });
68270
68271             // downloaded
68272             var osm = context.connection();
68273             var dataDownloaded = [];
68274             if (osm && showDownloaded) {
68275               var rtree = osm.caches('get').tile.rtree;
68276               dataDownloaded = rtree.all().map(function (bbox) {
68277                 return {
68278                   type: 'Feature',
68279                   properties: { id: bbox.id },
68280                   geometry: {
68281                     type: 'Polygon',
68282                     coordinates: [[
68283                       [ bbox.minX, bbox.minY ],
68284                       [ bbox.minX, bbox.maxY ],
68285                       [ bbox.maxX, bbox.maxY ],
68286                       [ bbox.maxX, bbox.minY ],
68287                       [ bbox.minX, bbox.minY ]
68288                     ]]
68289                   }
68290                 };
68291               });
68292             }
68293
68294             var downloaded = layer
68295               .selectAll('path.debug-downloaded')
68296               .data(showDownloaded ? dataDownloaded : []);
68297
68298             downloaded.exit()
68299               .remove();
68300
68301             downloaded.enter()
68302               .append('path')
68303               .attr('class', 'debug-downloaded debug purple');
68304
68305             // update
68306             layer.selectAll('path')
68307               .attr('d', svgPath(projection).geojson);
68308           }
68309
68310
68311           // This looks strange because `enabled` methods on other layers are
68312           // chainable getter/setters, and this one is just a getter.
68313           drawDebug.enabled = function() {
68314             if (!arguments.length) {
68315               return context.getDebug('tile') ||
68316                 context.getDebug('collision') ||
68317                 context.getDebug('imagery') ||
68318                 context.getDebug('target') ||
68319                 context.getDebug('downloaded');
68320             } else {
68321                 return this;
68322             }
68323           };
68324
68325
68326           return drawDebug;
68327         }
68328
68329         var _layerEnabled = false;
68330         var _qaService;
68331
68332         function svgKeepRight(projection, context, dispatch) {
68333           var throttledRedraw = throttle(function () { return dispatch.call('change'); }, 1000);
68334           var minZoom = 12;
68335
68336           var touchLayer = select(null);
68337           var drawLayer = select(null);
68338           var layerVisible = false;
68339
68340           function markerPath(selection, klass) {
68341             selection
68342               .attr('class', klass)
68343               .attr('transform', 'translate(-4, -24)')
68344               .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');
68345           }
68346
68347           // Loosely-coupled keepRight service for fetching issues.
68348           function getService() {
68349             if (services.keepRight && !_qaService) {
68350               _qaService = services.keepRight;
68351               _qaService.on('loaded', throttledRedraw);
68352             } else if (!services.keepRight && _qaService) {
68353               _qaService = null;
68354             }
68355
68356             return _qaService;
68357           }
68358
68359           // Show the markers
68360           function editOn() {
68361             if (!layerVisible) {
68362               layerVisible = true;
68363               drawLayer
68364                 .style('display', 'block');
68365             }
68366           }
68367
68368           // Immediately remove the markers and their touch targets
68369           function editOff() {
68370             if (layerVisible) {
68371               layerVisible = false;
68372               drawLayer
68373                 .style('display', 'none');
68374               drawLayer.selectAll('.qaItem.keepRight')
68375                 .remove();
68376               touchLayer.selectAll('.qaItem.keepRight')
68377                 .remove();
68378             }
68379           }
68380
68381           // Enable the layer.  This shows the markers and transitions them to visible.
68382           function layerOn() {
68383             editOn();
68384
68385             drawLayer
68386               .style('opacity', 0)
68387               .transition()
68388               .duration(250)
68389               .style('opacity', 1)
68390               .on('end interrupt', function () { return dispatch.call('change'); });
68391           }
68392
68393           // Disable the layer.  This transitions the layer invisible and then hides the markers.
68394           function layerOff() {
68395             throttledRedraw.cancel();
68396             drawLayer.interrupt();
68397             touchLayer.selectAll('.qaItem.keepRight')
68398               .remove();
68399
68400             drawLayer
68401               .transition()
68402               .duration(250)
68403               .style('opacity', 0)
68404               .on('end interrupt', function () {
68405                 editOff();
68406                 dispatch.call('change');
68407               });
68408           }
68409
68410           // Update the issue markers
68411           function updateMarkers() {
68412             if (!layerVisible || !_layerEnabled) { return; }
68413
68414             var service = getService();
68415             var selectedID = context.selectedErrorID();
68416             var data = (service ? service.getItems(projection) : []);
68417             var getTransform = svgPointTransform(projection);
68418
68419             // Draw markers..
68420             var markers = drawLayer.selectAll('.qaItem.keepRight')
68421               .data(data, function (d) { return d.id; });
68422
68423             // exit
68424             markers.exit()
68425               .remove();
68426
68427             // enter
68428             var markersEnter = markers.enter()
68429               .append('g')
68430                 .attr('class', function (d) { return ("qaItem " + (d.service) + " itemId-" + (d.id) + " itemType-" + (d.parentIssueType)); });
68431
68432             markersEnter
68433               .append('ellipse')
68434                 .attr('cx', 0.5)
68435                 .attr('cy', 1)
68436                 .attr('rx', 6.5)
68437                 .attr('ry', 3)
68438                 .attr('class', 'stroke');
68439
68440             markersEnter
68441               .append('path')
68442                 .call(markerPath, 'shadow');
68443
68444             markersEnter
68445               .append('use')
68446                 .attr('class', 'qaItem-fill')
68447                 .attr('width', '20px')
68448                 .attr('height', '20px')
68449                 .attr('x', '-8px')
68450                 .attr('y', '-22px')
68451                 .attr('xlink:href', '#iD-icon-bolt');
68452
68453             // update
68454             markers
68455               .merge(markersEnter)
68456               .sort(sortY)
68457                 .classed('selected', function (d) { return d.id === selectedID; })
68458                 .attr('transform', getTransform);
68459
68460
68461             // Draw targets..
68462             if (touchLayer.empty()) { return; }
68463             var fillClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
68464
68465             var targets = touchLayer.selectAll('.qaItem.keepRight')
68466               .data(data, function (d) { return d.id; });
68467
68468             // exit
68469             targets.exit()
68470               .remove();
68471
68472             // enter/update
68473             targets.enter()
68474               .append('rect')
68475                 .attr('width', '20px')
68476                 .attr('height', '20px')
68477                 .attr('x', '-8px')
68478                 .attr('y', '-22px')
68479               .merge(targets)
68480               .sort(sortY)
68481                 .attr('class', function (d) { return ("qaItem " + (d.service) + " target " + fillClass + " itemId-" + (d.id)); })
68482                 .attr('transform', getTransform);
68483
68484
68485             function sortY(a, b) {
68486               return (a.id === selectedID) ? 1
68487                 : (b.id === selectedID) ? -1
68488                 : (a.severity === 'error' && b.severity !== 'error') ? 1
68489                 : (b.severity === 'error' && a.severity !== 'error') ? -1
68490                 : b.loc[1] - a.loc[1];
68491             }
68492           }
68493
68494           // Draw the keepRight layer and schedule loading issues and updating markers.
68495           function drawKeepRight(selection) {
68496             var service = getService();
68497
68498             var surface = context.surface();
68499             if (surface && !surface.empty()) {
68500               touchLayer = surface.selectAll('.data-layer.touch .layer-touch.markers');
68501             }
68502
68503             drawLayer = selection.selectAll('.layer-keepRight')
68504               .data(service ? [0] : []);
68505
68506             drawLayer.exit()
68507               .remove();
68508
68509             drawLayer = drawLayer.enter()
68510               .append('g')
68511                 .attr('class', 'layer-keepRight')
68512                 .style('display', _layerEnabled ? 'block' : 'none')
68513               .merge(drawLayer);
68514
68515             if (_layerEnabled) {
68516               if (service && ~~context.map().zoom() >= minZoom) {
68517                 editOn();
68518                 service.loadIssues(projection);
68519                 updateMarkers();
68520               } else {
68521                 editOff();
68522               }
68523             }
68524           }
68525
68526           // Toggles the layer on and off
68527           drawKeepRight.enabled = function(val) {
68528             if (!arguments.length) { return _layerEnabled; }
68529
68530             _layerEnabled = val;
68531             if (_layerEnabled) {
68532               layerOn();
68533             } else {
68534               layerOff();
68535               if (context.selectedErrorID()) {
68536                 context.enter(modeBrowse(context));
68537               }
68538             }
68539
68540             dispatch.call('change');
68541             return this;
68542           };
68543
68544           drawKeepRight.supported = function () { return !!getService(); };
68545
68546           return drawKeepRight;
68547         }
68548
68549         function svgGeolocate(projection) {
68550             var layer = select(null);
68551             var _position;
68552
68553
68554             function init() {
68555                 if (svgGeolocate.initialized) { return; }  // run once
68556                 svgGeolocate.enabled = false;
68557                 svgGeolocate.initialized = true;
68558             }
68559
68560             function showLayer() {
68561                 layer.style('display', 'block');
68562             }
68563
68564
68565             function hideLayer() {
68566                 layer
68567                     .transition()
68568                     .duration(250)
68569                     .style('opacity', 0);
68570             }
68571
68572             function layerOn() {
68573                 layer
68574                     .style('opacity', 0)
68575                     .transition()
68576                     .duration(250)
68577                     .style('opacity', 1);
68578
68579             }
68580
68581             function layerOff() {
68582                 layer.style('display', 'none');
68583             }
68584
68585             function transform(d) {
68586                 return svgPointTransform(projection)(d);
68587             }
68588
68589             function accuracy(accuracy, loc) { // converts accuracy to pixels...
68590                 var degreesRadius = geoMetersToLat(accuracy),
68591                     tangentLoc = [loc[0], loc[1] + degreesRadius],
68592                     projectedTangent = projection(tangentLoc),
68593                     projectedLoc = projection([loc[0], loc[1]]);
68594
68595                 // southern most point will have higher pixel value...
68596                return Math.round(projectedLoc[1] - projectedTangent[1]).toString();
68597             }
68598
68599             function update() {
68600                 var geolocation = { loc: [_position.coords.longitude, _position.coords.latitude] };
68601
68602                 var groups = layer.selectAll('.geolocations').selectAll('.geolocation')
68603                     .data([geolocation]);
68604
68605                 groups.exit()
68606                     .remove();
68607
68608                 var pointsEnter = groups.enter()
68609                     .append('g')
68610                     .attr('class', 'geolocation');
68611
68612                 pointsEnter
68613                     .append('circle')
68614                     .attr('class', 'geolocate-radius')
68615                     .attr('dx', '0')
68616                     .attr('dy', '0')
68617                     .attr('fill', 'rgb(15,128,225)')
68618                     .attr('fill-opacity', '0.3')
68619                     .attr('r', '0');
68620
68621                 pointsEnter
68622                     .append('circle')
68623                     .attr('dx', '0')
68624                     .attr('dy', '0')
68625                     .attr('fill', 'rgb(15,128,225)')
68626                     .attr('stroke', 'white')
68627                     .attr('stroke-width', '1.5')
68628                     .attr('r', '6');
68629
68630                 groups.merge(pointsEnter)
68631                     .attr('transform', transform);
68632
68633                 layer.select('.geolocate-radius').attr('r', accuracy(_position.coords.accuracy, geolocation.loc));
68634             }
68635
68636             function drawLocation(selection) {
68637                 var enabled = svgGeolocate.enabled;
68638
68639                 layer = selection.selectAll('.layer-geolocate')
68640                     .data([0]);
68641
68642                 layer.exit()
68643                     .remove();
68644
68645                 var layerEnter = layer.enter()
68646                     .append('g')
68647                     .attr('class', 'layer-geolocate')
68648                     .style('display', enabled ? 'block' : 'none');
68649
68650                 layerEnter
68651                     .append('g')
68652                     .attr('class', 'geolocations');
68653
68654                 layer = layerEnter
68655                     .merge(layer);
68656
68657                 if (enabled) {
68658                     update();
68659                 } else {
68660                     layerOff();
68661                 }
68662             }
68663
68664             drawLocation.enabled = function (position, enabled) {
68665                 if (!arguments.length) { return svgGeolocate.enabled; }
68666                 _position = position;
68667                 svgGeolocate.enabled = enabled;
68668                 if (svgGeolocate.enabled) {
68669                     showLayer();
68670                     layerOn();
68671                 } else {
68672                     hideLayer();
68673                 }
68674                 return this;
68675             };
68676
68677             init();
68678             return drawLocation;
68679         }
68680
68681         function svgLabels(projection, context) {
68682             var path = d3_geoPath(projection);
68683             var detected = utilDetect();
68684             var baselineHack = (detected.ie ||
68685                 detected.browser.toLowerCase() === 'edge' ||
68686                 (detected.browser.toLowerCase() === 'firefox' && detected.version >= 70));
68687             var _rdrawn = new RBush();
68688             var _rskipped = new RBush();
68689             var _textWidthCache = {};
68690             var _entitybboxes = {};
68691
68692             // Listed from highest to lowest priority
68693             var labelStack = [
68694                 ['line', 'aeroway', '*', 12],
68695                 ['line', 'highway', 'motorway', 12],
68696                 ['line', 'highway', 'trunk', 12],
68697                 ['line', 'highway', 'primary', 12],
68698                 ['line', 'highway', 'secondary', 12],
68699                 ['line', 'highway', 'tertiary', 12],
68700                 ['line', 'highway', '*', 12],
68701                 ['line', 'railway', '*', 12],
68702                 ['line', 'waterway', '*', 12],
68703                 ['area', 'aeroway', '*', 12],
68704                 ['area', 'amenity', '*', 12],
68705                 ['area', 'building', '*', 12],
68706                 ['area', 'historic', '*', 12],
68707                 ['area', 'leisure', '*', 12],
68708                 ['area', 'man_made', '*', 12],
68709                 ['area', 'natural', '*', 12],
68710                 ['area', 'shop', '*', 12],
68711                 ['area', 'tourism', '*', 12],
68712                 ['area', 'camp_site', '*', 12],
68713                 ['point', 'aeroway', '*', 10],
68714                 ['point', 'amenity', '*', 10],
68715                 ['point', 'building', '*', 10],
68716                 ['point', 'historic', '*', 10],
68717                 ['point', 'leisure', '*', 10],
68718                 ['point', 'man_made', '*', 10],
68719                 ['point', 'natural', '*', 10],
68720                 ['point', 'shop', '*', 10],
68721                 ['point', 'tourism', '*', 10],
68722                 ['point', 'camp_site', '*', 10],
68723                 ['line', 'name', '*', 12],
68724                 ['area', 'name', '*', 12],
68725                 ['point', 'name', '*', 10]
68726             ];
68727
68728
68729             function shouldSkipIcon(preset) {
68730                 var noIcons = ['building', 'landuse', 'natural'];
68731                 return noIcons.some(function(s) {
68732                     return preset.id.indexOf(s) >= 0;
68733                 });
68734             }
68735
68736
68737             function get(array, prop) {
68738                 return function(d, i) { return array[i][prop]; };
68739             }
68740
68741
68742             function textWidth(text, size, elem) {
68743                 var c = _textWidthCache[size];
68744                 if (!c) { c = _textWidthCache[size] = {}; }
68745
68746                 if (c[text]) {
68747                     return c[text];
68748
68749                 } else if (elem) {
68750                     c[text] = elem.getComputedTextLength();
68751                     return c[text];
68752
68753                 } else {
68754                     var str = encodeURIComponent(text).match(/%[CDEFcdef]/g);
68755                     if (str === null) {
68756                         return size / 3 * 2 * text.length;
68757                     } else {
68758                         return size / 3 * (2 * text.length + str.length);
68759                     }
68760                 }
68761             }
68762
68763
68764             function drawLinePaths(selection, entities, filter, classes, labels) {
68765                 var paths = selection.selectAll('path')
68766                     .filter(filter)
68767                     .data(entities, osmEntity.key);
68768
68769                 // exit
68770                 paths.exit()
68771                     .remove();
68772
68773                 // enter/update
68774                 paths.enter()
68775                     .append('path')
68776                     .style('stroke-width', get(labels, 'font-size'))
68777                     .attr('id', function(d) { return 'ideditor-labelpath-' + d.id; })
68778                     .attr('class', classes)
68779                     .merge(paths)
68780                     .attr('d', get(labels, 'lineString'));
68781             }
68782
68783
68784             function drawLineLabels(selection, entities, filter, classes, labels) {
68785                 var texts = selection.selectAll('text.' + classes)
68786                     .filter(filter)
68787                     .data(entities, osmEntity.key);
68788
68789                 // exit
68790                 texts.exit()
68791                     .remove();
68792
68793                 // enter
68794                 texts.enter()
68795                     .append('text')
68796                     .attr('class', function(d, i) { return classes + ' ' + labels[i].classes + ' ' + d.id; })
68797                     .attr('dy', baselineHack ? '0.35em' : null)
68798                     .append('textPath')
68799                     .attr('class', 'textpath');
68800
68801                 // update
68802                 selection.selectAll('text.' + classes).selectAll('.textpath')
68803                     .filter(filter)
68804                     .data(entities, osmEntity.key)
68805                     .attr('startOffset', '50%')
68806                     .attr('xlink:href', function(d) { return '#ideditor-labelpath-' + d.id; })
68807                     .text(utilDisplayNameForPath);
68808             }
68809
68810
68811             function drawPointLabels(selection, entities, filter, classes, labels) {
68812                 var texts = selection.selectAll('text.' + classes)
68813                     .filter(filter)
68814                     .data(entities, osmEntity.key);
68815
68816                 // exit
68817                 texts.exit()
68818                     .remove();
68819
68820                 // enter/update
68821                 texts.enter()
68822                     .append('text')
68823                     .attr('class', function(d, i) {
68824                         return classes + ' ' + labels[i].classes + ' ' + d.id;
68825                     })
68826                     .merge(texts)
68827                     .attr('x', get(labels, 'x'))
68828                     .attr('y', get(labels, 'y'))
68829                     .style('text-anchor', get(labels, 'textAnchor'))
68830                     .text(utilDisplayName)
68831                     .each(function(d, i) {
68832                         textWidth(utilDisplayName(d), labels[i].height, this);
68833                     });
68834             }
68835
68836
68837             function drawAreaLabels(selection, entities, filter, classes, labels) {
68838                 entities = entities.filter(hasText);
68839                 labels = labels.filter(hasText);
68840                 drawPointLabels(selection, entities, filter, classes, labels);
68841
68842                 function hasText(d, i) {
68843                     return labels[i].hasOwnProperty('x') && labels[i].hasOwnProperty('y');
68844                 }
68845             }
68846
68847
68848             function drawAreaIcons(selection, entities, filter, classes, labels) {
68849                 var icons = selection.selectAll('use.' + classes)
68850                     .filter(filter)
68851                     .data(entities, osmEntity.key);
68852
68853                 // exit
68854                 icons.exit()
68855                     .remove();
68856
68857                 // enter/update
68858                 icons.enter()
68859                     .append('use')
68860                     .attr('class', 'icon ' + classes)
68861                     .attr('width', '17px')
68862                     .attr('height', '17px')
68863                     .merge(icons)
68864                     .attr('transform', get(labels, 'transform'))
68865                     .attr('xlink:href', function(d) {
68866                         var preset = _mainPresetIndex.match(d, context.graph());
68867                         var picon = preset && preset.icon;
68868
68869                         if (!picon) {
68870                             return '';
68871                         } else {
68872                             var isMaki = /^maki-/.test(picon);
68873                             return '#' + picon + (isMaki ? '-15' : '');
68874                         }
68875                     });
68876             }
68877
68878
68879             function drawCollisionBoxes(selection, rtree, which) {
68880                 var classes = 'debug ' + which + ' ' + (which === 'debug-skipped' ? 'orange' : 'yellow');
68881
68882                 var gj = [];
68883                 if (context.getDebug('collision')) {
68884                     gj = rtree.all().map(function(d) {
68885                         return { type: 'Polygon', coordinates: [[
68886                             [d.minX, d.minY],
68887                             [d.maxX, d.minY],
68888                             [d.maxX, d.maxY],
68889                             [d.minX, d.maxY],
68890                             [d.minX, d.minY]
68891                         ]]};
68892                     });
68893                 }
68894
68895                 var boxes = selection.selectAll('.' + which)
68896                     .data(gj);
68897
68898                 // exit
68899                 boxes.exit()
68900                     .remove();
68901
68902                 // enter/update
68903                 boxes.enter()
68904                     .append('path')
68905                     .attr('class', classes)
68906                     .merge(boxes)
68907                     .attr('d', d3_geoPath());
68908             }
68909
68910
68911             function drawLabels(selection, graph, entities, filter, dimensions, fullRedraw) {
68912                 var wireframe = context.surface().classed('fill-wireframe');
68913                 var zoom = geoScaleToZoom(projection.scale());
68914
68915                 var labelable = [];
68916                 var renderNodeAs = {};
68917                 var i, j, k, entity, geometry;
68918
68919                 for (i = 0; i < labelStack.length; i++) {
68920                     labelable.push([]);
68921                 }
68922
68923                 if (fullRedraw) {
68924                     _rdrawn.clear();
68925                     _rskipped.clear();
68926                     _entitybboxes = {};
68927
68928                 } else {
68929                     for (i = 0; i < entities.length; i++) {
68930                         entity = entities[i];
68931                         var toRemove = []
68932                             .concat(_entitybboxes[entity.id] || [])
68933                             .concat(_entitybboxes[entity.id + 'I'] || []);
68934
68935                         for (j = 0; j < toRemove.length; j++) {
68936                             _rdrawn.remove(toRemove[j]);
68937                             _rskipped.remove(toRemove[j]);
68938                         }
68939                     }
68940                 }
68941
68942                 // Loop through all the entities to do some preprocessing
68943                 for (i = 0; i < entities.length; i++) {
68944                     entity = entities[i];
68945                     geometry = entity.geometry(graph);
68946
68947                     // Insert collision boxes around interesting points/vertices
68948                     if (geometry === 'point' || (geometry === 'vertex' && isInterestingVertex(entity))) {
68949                         var hasDirections = entity.directions(graph, projection).length;
68950                         var markerPadding;
68951
68952                         if (!wireframe && geometry === 'point' && !(zoom >= 18 && hasDirections)) {
68953                             renderNodeAs[entity.id] = 'point';
68954                             markerPadding = 20;   // extra y for marker height
68955                         } else {
68956                             renderNodeAs[entity.id] = 'vertex';
68957                             markerPadding = 0;
68958                         }
68959
68960                         var coord = projection(entity.loc);
68961                         var nodePadding = 10;
68962                         var bbox = {
68963                             minX: coord[0] - nodePadding,
68964                             minY: coord[1] - nodePadding - markerPadding,
68965                             maxX: coord[0] + nodePadding,
68966                             maxY: coord[1] + nodePadding
68967                         };
68968
68969                         doInsert(bbox, entity.id + 'P');
68970                     }
68971
68972                     // From here on, treat vertices like points
68973                     if (geometry === 'vertex') {
68974                         geometry = 'point';
68975                     }
68976
68977                     // Determine which entities are label-able
68978                     var preset = geometry === 'area' && _mainPresetIndex.match(entity, graph);
68979                     var icon = preset && !shouldSkipIcon(preset) && preset.icon;
68980
68981                     if (!icon && !utilDisplayName(entity))
68982                         { continue; }
68983
68984                     for (k = 0; k < labelStack.length; k++) {
68985                         var matchGeom = labelStack[k][0];
68986                         var matchKey = labelStack[k][1];
68987                         var matchVal = labelStack[k][2];
68988                         var hasVal = entity.tags[matchKey];
68989
68990                         if (geometry === matchGeom && hasVal && (matchVal === '*' || matchVal === hasVal)) {
68991                             labelable[k].push(entity);
68992                             break;
68993                         }
68994                     }
68995                 }
68996
68997                 var positions = {
68998                     point: [],
68999                     line: [],
69000                     area: []
69001                 };
69002
69003                 var labelled = {
69004                     point: [],
69005                     line: [],
69006                     area: []
69007                 };
69008
69009                 // Try and find a valid label for labellable entities
69010                 for (k = 0; k < labelable.length; k++) {
69011                     var fontSize = labelStack[k][3];
69012
69013                     for (i = 0; i < labelable[k].length; i++) {
69014                         entity = labelable[k][i];
69015                         geometry = entity.geometry(graph);
69016
69017                         var getName = (geometry === 'line') ? utilDisplayNameForPath : utilDisplayName;
69018                         var name = getName(entity);
69019                         var width = name && textWidth(name, fontSize);
69020                         var p = null;
69021
69022                         if (geometry === 'point' || geometry === 'vertex') {
69023                             // no point or vertex labels in wireframe mode
69024                             // no vertex labels at low zooms (vertices have no icons)
69025                             if (wireframe) { continue; }
69026                             var renderAs = renderNodeAs[entity.id];
69027                             if (renderAs === 'vertex' && zoom < 17) { continue; }
69028
69029                             p = getPointLabel(entity, width, fontSize, renderAs);
69030
69031                         } else if (geometry === 'line') {
69032                             p = getLineLabel(entity, width, fontSize);
69033
69034                         } else if (geometry === 'area') {
69035                             p = getAreaLabel(entity, width, fontSize);
69036                         }
69037
69038                         if (p) {
69039                             if (geometry === 'vertex') { geometry = 'point'; }  // treat vertex like point
69040                             p.classes = geometry + ' tag-' + labelStack[k][1];
69041                             positions[geometry].push(p);
69042                             labelled[geometry].push(entity);
69043                         }
69044                     }
69045                 }
69046
69047
69048                 function isInterestingVertex(entity) {
69049                     var selectedIDs = context.selectedIDs();
69050
69051                     return entity.hasInterestingTags() ||
69052                         entity.isEndpoint(graph) ||
69053                         entity.isConnected(graph) ||
69054                         selectedIDs.indexOf(entity.id) !== -1 ||
69055                         graph.parentWays(entity).some(function(parent) {
69056                             return selectedIDs.indexOf(parent.id) !== -1;
69057                         });
69058                 }
69059
69060
69061                 function getPointLabel(entity, width, height, geometry) {
69062                     var y = (geometry === 'point' ? -12 : 0);
69063                     var pointOffsets = {
69064                         ltr: [15, y, 'start'],
69065                         rtl: [-15, y, 'end']
69066                     };
69067
69068                     var textDirection = _mainLocalizer.textDirection();
69069
69070                     var coord = projection(entity.loc);
69071                     var textPadding = 2;
69072                     var offset = pointOffsets[textDirection];
69073                     var p = {
69074                         height: height,
69075                         width: width,
69076                         x: coord[0] + offset[0],
69077                         y: coord[1] + offset[1],
69078                         textAnchor: offset[2]
69079                     };
69080
69081                     // insert a collision box for the text label..
69082                     var bbox;
69083                     if (textDirection === 'rtl') {
69084                         bbox = {
69085                             minX: p.x - width - textPadding,
69086                             minY: p.y - (height / 2) - textPadding,
69087                             maxX: p.x + textPadding,
69088                             maxY: p.y + (height / 2) + textPadding
69089                         };
69090                     } else {
69091                         bbox = {
69092                             minX: p.x - textPadding,
69093                             minY: p.y - (height / 2) - textPadding,
69094                             maxX: p.x + width + textPadding,
69095                             maxY: p.y + (height / 2) + textPadding
69096                         };
69097                     }
69098
69099                     if (tryInsert([bbox], entity.id, true)) {
69100                         return p;
69101                     }
69102                 }
69103
69104
69105                 function getLineLabel(entity, width, height) {
69106                     var viewport = geoExtent(context.projection.clipExtent()).polygon();
69107                     var points = graph.childNodes(entity)
69108                         .map(function(node) { return projection(node.loc); });
69109                     var length = geoPathLength(points);
69110
69111                     if (length < width + 20) { return; }
69112
69113                     // % along the line to attempt to place the label
69114                     var lineOffsets = [50, 45, 55, 40, 60, 35, 65, 30, 70,
69115                                        25, 75, 20, 80, 15, 95, 10, 90, 5, 95];
69116                     var padding = 3;
69117
69118                     for (var i = 0; i < lineOffsets.length; i++) {
69119                         var offset = lineOffsets[i];
69120                         var middle = offset / 100 * length;
69121                         var start = middle - width / 2;
69122
69123                         if (start < 0 || start + width > length) { continue; }
69124
69125                         // generate subpath and ignore paths that are invalid or don't cross viewport.
69126                         var sub = subpath(points, start, start + width);
69127                         if (!sub || !geoPolygonIntersectsPolygon(viewport, sub, true)) {
69128                             continue;
69129                         }
69130
69131                         var isReverse = reverse(sub);
69132                         if (isReverse) {
69133                             sub = sub.reverse();
69134                         }
69135
69136                         var bboxes = [];
69137                         var boxsize = (height + 2) / 2;
69138
69139                         for (var j = 0; j < sub.length - 1; j++) {
69140                             var a = sub[j];
69141                             var b = sub[j + 1];
69142
69143                             // split up the text into small collision boxes
69144                             var num = Math.max(1, Math.floor(geoVecLength(a, b) / boxsize / 2));
69145
69146                             for (var box = 0; box < num; box++) {
69147                                 var p = geoVecInterp(a, b, box / num);
69148                                 var x0 = p[0] - boxsize - padding;
69149                                 var y0 = p[1] - boxsize - padding;
69150                                 var x1 = p[0] + boxsize + padding;
69151                                 var y1 = p[1] + boxsize + padding;
69152
69153                                 bboxes.push({
69154                                     minX: Math.min(x0, x1),
69155                                     minY: Math.min(y0, y1),
69156                                     maxX: Math.max(x0, x1),
69157                                     maxY: Math.max(y0, y1)
69158                                 });
69159                             }
69160                         }
69161
69162                         if (tryInsert(bboxes, entity.id, false)) {   // accept this one
69163                             return {
69164                                 'font-size': height + 2,
69165                                 lineString: lineString(sub),
69166                                 startOffset: offset + '%'
69167                             };
69168                         }
69169                     }
69170
69171                     function reverse(p) {
69172                         var angle = Math.atan2(p[1][1] - p[0][1], p[1][0] - p[0][0]);
69173                         return !(p[0][0] < p[p.length - 1][0] && angle < Math.PI/2 && angle > -Math.PI/2);
69174                     }
69175
69176                     function lineString(points) {
69177                         return 'M' + points.join('L');
69178                     }
69179
69180                     function subpath(points, from, to) {
69181                         var sofar = 0;
69182                         var start, end, i0, i1;
69183
69184                         for (var i = 0; i < points.length - 1; i++) {
69185                             var a = points[i];
69186                             var b = points[i + 1];
69187                             var current = geoVecLength(a, b);
69188                             var portion;
69189                             if (!start && sofar + current >= from) {
69190                                 portion = (from - sofar) / current;
69191                                 start = [
69192                                     a[0] + portion * (b[0] - a[0]),
69193                                     a[1] + portion * (b[1] - a[1])
69194                                 ];
69195                                 i0 = i + 1;
69196                             }
69197                             if (!end && sofar + current >= to) {
69198                                 portion = (to - sofar) / current;
69199                                 end = [
69200                                     a[0] + portion * (b[0] - a[0]),
69201                                     a[1] + portion * (b[1] - a[1])
69202                                 ];
69203                                 i1 = i + 1;
69204                             }
69205                             sofar += current;
69206                         }
69207
69208                         var result = points.slice(i0, i1);
69209                         result.unshift(start);
69210                         result.push(end);
69211                         return result;
69212                     }
69213                 }
69214
69215
69216                 function getAreaLabel(entity, width, height) {
69217                     var centroid = path.centroid(entity.asGeoJSON(graph, true));
69218                     var extent = entity.extent(graph);
69219                     var areaWidth = projection(extent[1])[0] - projection(extent[0])[0];
69220
69221                     if (isNaN(centroid[0]) || areaWidth < 20) { return; }
69222
69223                     var preset = _mainPresetIndex.match(entity, context.graph());
69224                     var picon = preset && preset.icon;
69225                     var iconSize = 17;
69226                     var padding = 2;
69227                     var p = {};
69228
69229                     if (picon) {  // icon and label..
69230                         if (addIcon()) {
69231                             addLabel(iconSize + padding);
69232                             return p;
69233                         }
69234                     } else {   // label only..
69235                         if (addLabel(0)) {
69236                             return p;
69237                         }
69238                     }
69239
69240
69241                     function addIcon() {
69242                         var iconX = centroid[0] - (iconSize / 2);
69243                         var iconY = centroid[1] - (iconSize / 2);
69244                         var bbox = {
69245                             minX: iconX,
69246                             minY: iconY,
69247                             maxX: iconX + iconSize,
69248                             maxY: iconY + iconSize
69249                         };
69250
69251                         if (tryInsert([bbox], entity.id + 'I', true)) {
69252                             p.transform = 'translate(' + iconX + ',' + iconY + ')';
69253                             return true;
69254                         }
69255                         return false;
69256                     }
69257
69258                     function addLabel(yOffset) {
69259                         if (width && areaWidth >= width + 20) {
69260                             var labelX = centroid[0];
69261                             var labelY = centroid[1] + yOffset;
69262                             var bbox = {
69263                                 minX: labelX - (width / 2) - padding,
69264                                 minY: labelY - (height / 2) - padding,
69265                                 maxX: labelX + (width / 2) + padding,
69266                                 maxY: labelY + (height / 2) + padding
69267                             };
69268
69269                             if (tryInsert([bbox], entity.id, true)) {
69270                                 p.x = labelX;
69271                                 p.y = labelY;
69272                                 p.textAnchor = 'middle';
69273                                 p.height = height;
69274                                 return true;
69275                             }
69276                         }
69277                         return false;
69278                     }
69279                 }
69280
69281
69282                 // force insert a singular bounding box
69283                 // singular box only, no array, id better be unique
69284                 function doInsert(bbox, id) {
69285                     bbox.id = id;
69286
69287                     var oldbox = _entitybboxes[id];
69288                     if (oldbox) {
69289                         _rdrawn.remove(oldbox);
69290                     }
69291                     _entitybboxes[id] = bbox;
69292                     _rdrawn.insert(bbox);
69293                 }
69294
69295
69296                 function tryInsert(bboxes, id, saveSkipped) {
69297                     var skipped = false;
69298
69299                     for (var i = 0; i < bboxes.length; i++) {
69300                         var bbox = bboxes[i];
69301                         bbox.id = id;
69302
69303                         // Check that label is visible
69304                         if (bbox.minX < 0 || bbox.minY < 0 || bbox.maxX > dimensions[0] || bbox.maxY > dimensions[1]) {
69305                             skipped = true;
69306                             break;
69307                         }
69308                         if (_rdrawn.collides(bbox)) {
69309                             skipped = true;
69310                             break;
69311                         }
69312                     }
69313
69314                     _entitybboxes[id] = bboxes;
69315
69316                     if (skipped) {
69317                         if (saveSkipped) {
69318                             _rskipped.load(bboxes);
69319                         }
69320                     } else {
69321                         _rdrawn.load(bboxes);
69322                     }
69323
69324                     return !skipped;
69325                 }
69326
69327
69328                 var layer = selection.selectAll('.layer-osm.labels');
69329                 layer.selectAll('.labels-group')
69330                     .data(['halo', 'label', 'debug'])
69331                     .enter()
69332                     .append('g')
69333                     .attr('class', function(d) { return 'labels-group ' + d; });
69334
69335                 var halo = layer.selectAll('.labels-group.halo');
69336                 var label = layer.selectAll('.labels-group.label');
69337                 var debug = layer.selectAll('.labels-group.debug');
69338
69339                 // points
69340                 drawPointLabels(label, labelled.point, filter, 'pointlabel', positions.point);
69341                 drawPointLabels(halo, labelled.point, filter, 'pointlabel-halo', positions.point);
69342
69343                 // lines
69344                 drawLinePaths(layer, labelled.line, filter, '', positions.line);
69345                 drawLineLabels(label, labelled.line, filter, 'linelabel', positions.line);
69346                 drawLineLabels(halo, labelled.line, filter, 'linelabel-halo', positions.line);
69347
69348                 // areas
69349                 drawAreaLabels(label, labelled.area, filter, 'arealabel', positions.area);
69350                 drawAreaLabels(halo, labelled.area, filter, 'arealabel-halo', positions.area);
69351                 drawAreaIcons(label, labelled.area, filter, 'areaicon', positions.area);
69352                 drawAreaIcons(halo, labelled.area, filter, 'areaicon-halo', positions.area);
69353
69354                 // debug
69355                 drawCollisionBoxes(debug, _rskipped, 'debug-skipped');
69356                 drawCollisionBoxes(debug, _rdrawn, 'debug-drawn');
69357
69358                 layer.call(filterLabels);
69359             }
69360
69361
69362             function filterLabels(selection) {
69363                 var drawLayer = selection.selectAll('.layer-osm.labels');
69364                 var layers = drawLayer.selectAll('.labels-group.halo, .labels-group.label');
69365
69366                 layers.selectAll('.nolabel')
69367                     .classed('nolabel', false);
69368
69369                 var mouse = context.map().mouse();
69370                 var graph = context.graph();
69371                 var selectedIDs = context.selectedIDs();
69372                 var ids = [];
69373                 var pad, bbox;
69374
69375                 // hide labels near the mouse
69376                 if (mouse) {
69377                     pad = 20;
69378                     bbox = { minX: mouse[0] - pad, minY: mouse[1] - pad, maxX: mouse[0] + pad, maxY: mouse[1] + pad };
69379                     var nearMouse = _rdrawn.search(bbox).map(function(entity) { return entity.id; });
69380                     ids.push.apply(ids, nearMouse);
69381                 }
69382
69383                 // hide labels on selected nodes (they look weird when dragging / haloed)
69384                 for (var i = 0; i < selectedIDs.length; i++) {
69385                     var entity = graph.hasEntity(selectedIDs[i]);
69386                     if (entity && entity.type === 'node') {
69387                         ids.push(selectedIDs[i]);
69388                     }
69389                 }
69390
69391                 layers.selectAll(utilEntitySelector(ids))
69392                     .classed('nolabel', true);
69393
69394
69395                 // draw the mouse bbox if debugging is on..
69396                 var debug = selection.selectAll('.labels-group.debug');
69397                 var gj = [];
69398                 if (context.getDebug('collision')) {
69399                     gj = bbox ? [{
69400                         type: 'Polygon',
69401                         coordinates: [[
69402                             [bbox.minX, bbox.minY],
69403                             [bbox.maxX, bbox.minY],
69404                             [bbox.maxX, bbox.maxY],
69405                             [bbox.minX, bbox.maxY],
69406                             [bbox.minX, bbox.minY]
69407                         ]]
69408                     }] : [];
69409                 }
69410
69411                 var box = debug.selectAll('.debug-mouse')
69412                     .data(gj);
69413
69414                 // exit
69415                 box.exit()
69416                     .remove();
69417
69418                 // enter/update
69419                 box.enter()
69420                     .append('path')
69421                     .attr('class', 'debug debug-mouse yellow')
69422                     .merge(box)
69423                     .attr('d', d3_geoPath());
69424             }
69425
69426
69427             var throttleFilterLabels = throttle(filterLabels, 100);
69428
69429
69430             drawLabels.observe = function(selection) {
69431                 var listener = function() { throttleFilterLabels(selection); };
69432                 selection.on('mousemove.hidelabels', listener);
69433                 context.on('enter.hidelabels', listener);
69434             };
69435
69436
69437             drawLabels.off = function(selection) {
69438                 throttleFilterLabels.cancel();
69439                 selection.on('mousemove.hidelabels', null);
69440                 context.on('enter.hidelabels', null);
69441             };
69442
69443
69444             return drawLabels;
69445         }
69446
69447         var _layerEnabled$1 = false;
69448         var _qaService$1;
69449
69450         function svgImproveOSM(projection, context, dispatch) {
69451           var throttledRedraw = throttle(function () { return dispatch.call('change'); }, 1000);
69452           var minZoom = 12;
69453
69454           var touchLayer = select(null);
69455           var drawLayer = select(null);
69456           var layerVisible = false;
69457
69458           function markerPath(selection, klass) {
69459             selection
69460               .attr('class', klass)
69461               .attr('transform', 'translate(-10, -28)')
69462               .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');
69463           }
69464
69465           // Loosely-coupled improveOSM service for fetching issues
69466           function getService() {
69467             if (services.improveOSM && !_qaService$1) {
69468               _qaService$1 = services.improveOSM;
69469               _qaService$1.on('loaded', throttledRedraw);
69470             } else if (!services.improveOSM && _qaService$1) {
69471               _qaService$1 = null;
69472             }
69473
69474             return _qaService$1;
69475           }
69476
69477           // Show the markers
69478           function editOn() {
69479             if (!layerVisible) {
69480               layerVisible = true;
69481               drawLayer
69482                 .style('display', 'block');
69483             }
69484           }
69485
69486           // Immediately remove the markers and their touch targets
69487           function editOff() {
69488             if (layerVisible) {
69489               layerVisible = false;
69490               drawLayer
69491                 .style('display', 'none');
69492               drawLayer.selectAll('.qaItem.improveOSM')
69493                 .remove();
69494               touchLayer.selectAll('.qaItem.improveOSM')
69495                 .remove();
69496             }
69497           }
69498
69499           // Enable the layer.  This shows the markers and transitions them to visible.
69500           function layerOn() {
69501             editOn();
69502
69503             drawLayer
69504               .style('opacity', 0)
69505               .transition()
69506               .duration(250)
69507               .style('opacity', 1)
69508               .on('end interrupt', function () { return dispatch.call('change'); });
69509           }
69510
69511           // Disable the layer.  This transitions the layer invisible and then hides the markers.
69512           function layerOff() {
69513             throttledRedraw.cancel();
69514             drawLayer.interrupt();
69515             touchLayer.selectAll('.qaItem.improveOSM')
69516               .remove();
69517
69518             drawLayer
69519               .transition()
69520               .duration(250)
69521               .style('opacity', 0)
69522               .on('end interrupt', function () {
69523                 editOff();
69524                 dispatch.call('change');
69525               });
69526           }
69527
69528           // Update the issue markers
69529           function updateMarkers() {
69530             if (!layerVisible || !_layerEnabled$1) { return; }
69531
69532             var service = getService();
69533             var selectedID = context.selectedErrorID();
69534             var data = (service ? service.getItems(projection) : []);
69535             var getTransform = svgPointTransform(projection);
69536
69537             // Draw markers..
69538             var markers = drawLayer.selectAll('.qaItem.improveOSM')
69539               .data(data, function (d) { return d.id; });
69540
69541             // exit
69542             markers.exit()
69543               .remove();
69544
69545             // enter
69546             var markersEnter = markers.enter()
69547               .append('g')
69548                 .attr('class', function (d) { return ("qaItem " + (d.service) + " itemId-" + (d.id) + " itemType-" + (d.itemType)); });
69549
69550             markersEnter
69551               .append('polygon')
69552                 .call(markerPath, 'shadow');
69553
69554             markersEnter
69555               .append('ellipse')
69556                 .attr('cx', 0)
69557                 .attr('cy', 0)
69558                 .attr('rx', 4.5)
69559                 .attr('ry', 2)
69560                 .attr('class', 'stroke');
69561
69562             markersEnter
69563               .append('polygon')
69564                 .attr('fill', 'currentColor')
69565                 .call(markerPath, 'qaItem-fill');
69566
69567             markersEnter
69568               .append('use')
69569                 .attr('transform', 'translate(-6.5, -23)')
69570                 .attr('class', 'icon-annotation')
69571                 .attr('width', '13px')
69572                 .attr('height', '13px')
69573                 .attr('xlink:href', function (d) {
69574                   var picon = d.icon;
69575
69576                   if (!picon) {
69577                   return '';
69578                   } else {
69579                   var isMaki = /^maki-/.test(picon);
69580                   return ("#" + picon + (isMaki ? '-11' : ''));
69581                   }
69582                 });
69583
69584             // update
69585             markers
69586               .merge(markersEnter)
69587               .sort(sortY)
69588                 .classed('selected', function (d) { return d.id === selectedID; })
69589                 .attr('transform', getTransform);
69590
69591
69592             // Draw targets..
69593             if (touchLayer.empty()) { return; }
69594             var fillClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
69595
69596             var targets = touchLayer.selectAll('.qaItem.improveOSM')
69597               .data(data, function (d) { return d.id; });
69598
69599             // exit
69600             targets.exit()
69601               .remove();
69602
69603             // enter/update
69604             targets.enter()
69605               .append('rect')
69606                 .attr('width', '20px')
69607                 .attr('height', '30px')
69608                 .attr('x', '-10px')
69609                 .attr('y', '-28px')
69610               .merge(targets)
69611               .sort(sortY)
69612                 .attr('class', function (d) { return ("qaItem " + (d.service) + " target " + fillClass + " itemId-" + (d.id)); })
69613                 .attr('transform', getTransform);
69614
69615             function sortY(a, b) {
69616               return (a.id === selectedID) ? 1
69617                 : (b.id === selectedID) ? -1
69618                 : b.loc[1] - a.loc[1];
69619             }
69620           }
69621
69622           // Draw the ImproveOSM layer and schedule loading issues and updating markers.
69623           function drawImproveOSM(selection) {
69624             var service = getService();
69625
69626             var surface = context.surface();
69627             if (surface && !surface.empty()) {
69628               touchLayer = surface.selectAll('.data-layer.touch .layer-touch.markers');
69629             }
69630
69631             drawLayer = selection.selectAll('.layer-improveOSM')
69632               .data(service ? [0] : []);
69633
69634             drawLayer.exit()
69635               .remove();
69636
69637             drawLayer = drawLayer.enter()
69638               .append('g')
69639                 .attr('class', 'layer-improveOSM')
69640                 .style('display', _layerEnabled$1 ? 'block' : 'none')
69641               .merge(drawLayer);
69642
69643             if (_layerEnabled$1) {
69644               if (service && ~~context.map().zoom() >= minZoom) {
69645                 editOn();
69646                 service.loadIssues(projection);
69647                 updateMarkers();
69648               } else {
69649                 editOff();
69650               }
69651             }
69652           }
69653
69654           // Toggles the layer on and off
69655           drawImproveOSM.enabled = function(val) {
69656             if (!arguments.length) { return _layerEnabled$1; }
69657
69658             _layerEnabled$1 = val;
69659             if (_layerEnabled$1) {
69660               layerOn();
69661             } else {
69662               layerOff();
69663               if (context.selectedErrorID()) {
69664                 context.enter(modeBrowse(context));
69665               }
69666             }
69667
69668             dispatch.call('change');
69669             return this;
69670           };
69671
69672           drawImproveOSM.supported = function () { return !!getService(); };
69673
69674           return drawImproveOSM;
69675         }
69676
69677         var _layerEnabled$2 = false;
69678         var _qaService$2;
69679
69680         function svgOsmose(projection, context, dispatch) {
69681           var throttledRedraw = throttle(function () { return dispatch.call('change'); }, 1000);
69682           var minZoom = 12;
69683
69684           var touchLayer = select(null);
69685           var drawLayer = select(null);
69686           var layerVisible = false;
69687
69688           function markerPath(selection, klass) {
69689             selection
69690               .attr('class', klass)
69691               .attr('transform', 'translate(-10, -28)')
69692               .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');
69693           }
69694
69695           // Loosely-coupled osmose service for fetching issues
69696           function getService() {
69697             if (services.osmose && !_qaService$2) {
69698               _qaService$2 = services.osmose;
69699               _qaService$2.on('loaded', throttledRedraw);
69700             } else if (!services.osmose && _qaService$2) {
69701               _qaService$2 = null;
69702             }
69703
69704             return _qaService$2;
69705           }
69706
69707           // Show the markers
69708           function editOn() {
69709             if (!layerVisible) {
69710               layerVisible = true;
69711               drawLayer
69712                 .style('display', 'block');
69713             }
69714           }
69715
69716           // Immediately remove the markers and their touch targets
69717           function editOff() {
69718             if (layerVisible) {
69719               layerVisible = false;
69720               drawLayer
69721                 .style('display', 'none');
69722               drawLayer.selectAll('.qaItem.osmose')
69723                 .remove();
69724               touchLayer.selectAll('.qaItem.osmose')
69725                 .remove();
69726             }
69727           }
69728
69729           // Enable the layer.  This shows the markers and transitions them to visible.
69730           function layerOn() {
69731             editOn();
69732
69733             drawLayer
69734               .style('opacity', 0)
69735               .transition()
69736               .duration(250)
69737               .style('opacity', 1)
69738               .on('end interrupt', function () { return dispatch.call('change'); });
69739           }
69740
69741           // Disable the layer.  This transitions the layer invisible and then hides the markers.
69742           function layerOff() {
69743             throttledRedraw.cancel();
69744             drawLayer.interrupt();
69745             touchLayer.selectAll('.qaItem.osmose')
69746               .remove();
69747
69748             drawLayer
69749               .transition()
69750               .duration(250)
69751               .style('opacity', 0)
69752               .on('end interrupt', function () {
69753                 editOff();
69754                 dispatch.call('change');
69755               });
69756           }
69757
69758           // Update the issue markers
69759           function updateMarkers() {
69760             if (!layerVisible || !_layerEnabled$2) { return; }
69761
69762             var service = getService();
69763             var selectedID = context.selectedErrorID();
69764             var data = (service ? service.getItems(projection) : []);
69765             var getTransform = svgPointTransform(projection);
69766
69767             // Draw markers..
69768             var markers = drawLayer.selectAll('.qaItem.osmose')
69769               .data(data, function (d) { return d.id; });
69770
69771             // exit
69772             markers.exit()
69773               .remove();
69774
69775             // enter
69776             var markersEnter = markers.enter()
69777               .append('g')
69778                 .attr('class', function (d) { return ("qaItem " + (d.service) + " itemId-" + (d.id) + " itemType-" + (d.itemType)); });
69779
69780             markersEnter
69781               .append('polygon')
69782                 .call(markerPath, 'shadow');
69783
69784             markersEnter
69785               .append('ellipse')
69786                 .attr('cx', 0)
69787                 .attr('cy', 0)
69788                 .attr('rx', 4.5)
69789                 .attr('ry', 2)
69790                 .attr('class', 'stroke');
69791
69792             markersEnter
69793               .append('polygon')
69794                 .attr('fill', function (d) { return service.getColor(d.item); })
69795                 .call(markerPath, 'qaItem-fill');
69796
69797             markersEnter
69798               .append('use')
69799                 .attr('transform', 'translate(-6.5, -23)')
69800                 .attr('class', 'icon-annotation')
69801                 .attr('width', '13px')
69802                 .attr('height', '13px')
69803                 .attr('xlink:href', function (d) {
69804                   var picon = d.icon;
69805
69806                   if (!picon) {
69807                     return '';
69808                   } else {
69809                     var isMaki = /^maki-/.test(picon);
69810                     return ("#" + picon + (isMaki ? '-11' : ''));
69811                   }
69812                 });
69813
69814             // update
69815             markers
69816               .merge(markersEnter)
69817               .sort(sortY)
69818                 .classed('selected', function (d) { return d.id === selectedID; })
69819                 .attr('transform', getTransform);
69820
69821             // Draw targets..
69822             if (touchLayer.empty()) { return; }
69823             var fillClass = context.getDebug('target') ? 'pink' : 'nocolor';
69824
69825             var targets = touchLayer.selectAll('.qaItem.osmose')
69826               .data(data, function (d) { return d.id; });
69827
69828             // exit
69829             targets.exit()
69830               .remove();
69831
69832             // enter/update
69833             targets.enter()
69834               .append('rect')
69835                 .attr('width', '20px')
69836                 .attr('height', '30px')
69837                 .attr('x', '-10px')
69838                 .attr('y', '-28px')
69839               .merge(targets)
69840               .sort(sortY)
69841                 .attr('class', function (d) { return ("qaItem " + (d.service) + " target " + fillClass + " itemId-" + (d.id)); })
69842                 .attr('transform', getTransform);
69843
69844             function sortY(a, b) {
69845               return (a.id === selectedID) ? 1
69846                 : (b.id === selectedID) ? -1
69847                 : b.loc[1] - a.loc[1];
69848             }
69849           }
69850
69851           // Draw the Osmose layer and schedule loading issues and updating markers.
69852           function drawOsmose(selection) {
69853             var service = getService();
69854
69855             var surface = context.surface();
69856             if (surface && !surface.empty()) {
69857               touchLayer = surface.selectAll('.data-layer.touch .layer-touch.markers');
69858             }
69859
69860             drawLayer = selection.selectAll('.layer-osmose')
69861               .data(service ? [0] : []);
69862
69863             drawLayer.exit()
69864               .remove();
69865
69866             drawLayer = drawLayer.enter()
69867               .append('g')
69868                 .attr('class', 'layer-osmose')
69869                 .style('display', _layerEnabled$2 ? 'block' : 'none')
69870               .merge(drawLayer);
69871
69872             if (_layerEnabled$2) {
69873               if (service && ~~context.map().zoom() >= minZoom) {
69874                 editOn();
69875                 service.loadIssues(projection);
69876                 updateMarkers();
69877               } else {
69878                 editOff();
69879               }
69880             }
69881           }
69882
69883           // Toggles the layer on and off
69884           drawOsmose.enabled = function(val) {
69885             if (!arguments.length) { return _layerEnabled$2; }
69886
69887             _layerEnabled$2 = val;
69888             if (_layerEnabled$2) {
69889               // Strings supplied by Osmose fetched before showing layer for first time
69890               // NOTE: Currently no way to change locale in iD at runtime, would need to re-call this method if that's ever implemented
69891               // Also, If layer is toggled quickly multiple requests are sent
69892               getService().loadStrings()
69893                 .then(layerOn)
69894                 .catch(function (err) {
69895                   console.log(err); // eslint-disable-line no-console
69896                 });
69897             } else {
69898               layerOff();
69899               if (context.selectedErrorID()) {
69900                 context.enter(modeBrowse(context));
69901               }
69902             }
69903
69904             dispatch.call('change');
69905             return this;
69906           };
69907
69908           drawOsmose.supported = function () { return !!getService(); };
69909
69910           return drawOsmose;
69911         }
69912
69913         function svgStreetside(projection, context, dispatch) {
69914             var throttledRedraw = throttle(function () { dispatch.call('change'); }, 1000);
69915             var minZoom = 14;
69916             var minMarkerZoom = 16;
69917             var minViewfieldZoom = 18;
69918             var layer = select(null);
69919             var _viewerYaw = 0;
69920             var _selectedSequence = null;
69921             var _streetside;
69922
69923             /**
69924              * init().
69925              */
69926             function init() {
69927                 if (svgStreetside.initialized) { return; }  // run once
69928                 svgStreetside.enabled = false;
69929                 svgStreetside.initialized = true;
69930             }
69931
69932             /**
69933              * getService().
69934              */
69935             function getService() {
69936                 if (services.streetside && !_streetside) {
69937                     _streetside = services.streetside;
69938                     _streetside.event
69939                         .on('viewerChanged.svgStreetside', viewerChanged)
69940                         .on('loadedBubbles.svgStreetside', throttledRedraw);
69941                 } else if (!services.streetside && _streetside) {
69942                     _streetside = null;
69943                 }
69944
69945                 return _streetside;
69946             }
69947
69948             /**
69949              * showLayer().
69950              */
69951             function showLayer() {
69952                 var service = getService();
69953                 if (!service) { return; }
69954
69955                 editOn();
69956
69957                 layer
69958                     .style('opacity', 0)
69959                     .transition()
69960                     .duration(250)
69961                     .style('opacity', 1)
69962                     .on('end', function () { dispatch.call('change'); });
69963             }
69964
69965             /**
69966              * hideLayer().
69967              */
69968             function hideLayer() {
69969                 throttledRedraw.cancel();
69970
69971                 layer
69972                     .transition()
69973                     .duration(250)
69974                     .style('opacity', 0)
69975                     .on('end', editOff);
69976             }
69977
69978             /**
69979              * editOn().
69980              */
69981             function editOn() {
69982                 layer.style('display', 'block');
69983             }
69984
69985             /**
69986              * editOff().
69987              */
69988             function editOff() {
69989                 layer.selectAll('.viewfield-group').remove();
69990                 layer.style('display', 'none');
69991             }
69992
69993             /**
69994              * click() Handles 'bubble' point click event.
69995              */
69996             function click(d) {
69997                 var service = getService();
69998                 if (!service) { return; }
69999
70000                 // try to preserve the viewer rotation when staying on the same sequence
70001                 if (d.sequenceKey !== _selectedSequence) {
70002                     _viewerYaw = 0;  // reset
70003                 }
70004                 _selectedSequence = d.sequenceKey;
70005
70006                 service
70007                     .selectImage(context, d)
70008                     .then(function (response) {
70009                         if (response.status === 'ok'){
70010                             service.showViewer(context, _viewerYaw);
70011                         }
70012                     });
70013
70014
70015                 context.map().centerEase(d.loc);
70016             }
70017
70018             /**
70019              * mouseover().
70020              */
70021             function mouseover(d) {
70022                 var service = getService();
70023                 if (service) { service.setStyles(context, d); }
70024             }
70025
70026             /**
70027              * mouseout().
70028              */
70029             function mouseout() {
70030                 var service = getService();
70031                 if (service) { service.setStyles(context, null); }
70032             }
70033
70034             /**
70035              * transform().
70036              */
70037             function transform(d) {
70038                 var t = svgPointTransform(projection)(d);
70039                 var rot = d.ca + _viewerYaw;
70040                 if (rot) {
70041                     t += ' rotate(' + Math.floor(rot) + ',0,0)';
70042                 }
70043                 return t;
70044             }
70045
70046
70047             function viewerChanged() {
70048                 var service = getService();
70049                 if (!service) { return; }
70050
70051                 var viewer = service.viewer();
70052                 if (!viewer) { return; }
70053
70054                 // update viewfield rotation
70055                 _viewerYaw = viewer.getYaw();
70056
70057                 // avoid updating if the map is currently transformed
70058                 // e.g. during drags or easing.
70059                 if (context.map().isTransformed()) { return; }
70060
70061                 layer.selectAll('.viewfield-group.currentView')
70062                     .attr('transform', transform);
70063             }
70064
70065
70066             context.photos().on('change.streetside', update);
70067
70068             /**
70069              * update().
70070              */
70071             function update() {
70072                 var viewer = context.container().select('.photoviewer');
70073                 var selected = viewer.empty() ? undefined : viewer.datum();
70074                 var z = ~~context.map().zoom();
70075                 var showMarkers = (z >= minMarkerZoom);
70076                 var showViewfields = (z >= minViewfieldZoom);
70077                 var service = getService();
70078
70079                 var sequences = [];
70080                 var bubbles = [];
70081
70082                 if (context.photos().showsPanoramic()) {
70083                     sequences = (service ? service.sequences(projection) : []);
70084                     bubbles = (service && showMarkers ? service.bubbles(projection) : []);
70085                 }
70086
70087                 var traces = layer.selectAll('.sequences').selectAll('.sequence')
70088                     .data(sequences, function(d) { return d.properties.key; });
70089
70090                 // exit
70091                 traces.exit()
70092                     .remove();
70093
70094                 // enter/update
70095                 traces = traces.enter()
70096                     .append('path')
70097                     .attr('class', 'sequence')
70098                     .merge(traces)
70099                     .attr('d', svgPath(projection).geojson);
70100
70101
70102                 var groups = layer.selectAll('.markers').selectAll('.viewfield-group')
70103                     .data(bubbles, function(d) {
70104                         // force reenter once bubbles are attached to a sequence
70105                         return d.key + (d.sequenceKey ? 'v1' : 'v0');
70106                     });
70107
70108                 // exit
70109                 groups.exit()
70110                     .remove();
70111
70112                 // enter
70113                 var groupsEnter = groups.enter()
70114                     .append('g')
70115                     .attr('class', 'viewfield-group')
70116                     .on('mouseenter', mouseover)
70117                     .on('mouseleave', mouseout)
70118                     .on('click', click);
70119
70120                 groupsEnter
70121                     .append('g')
70122                     .attr('class', 'viewfield-scale');
70123
70124                 // update
70125                 var markers = groups
70126                     .merge(groupsEnter)
70127                     .sort(function(a, b) {
70128                         return (a === selected) ? 1
70129                             : (b === selected) ? -1
70130                             : b.loc[1] - a.loc[1];
70131                     })
70132                     .attr('transform', transform)
70133                     .select('.viewfield-scale');
70134
70135
70136                 markers.selectAll('circle')
70137                     .data([0])
70138                     .enter()
70139                     .append('circle')
70140                     .attr('dx', '0')
70141                     .attr('dy', '0')
70142                     .attr('r', '6');
70143
70144                 var viewfields = markers.selectAll('.viewfield')
70145                     .data(showViewfields ? [0] : []);
70146
70147                 viewfields.exit()
70148                     .remove();
70149
70150                 // viewfields may or may not be drawn...
70151                 // but if they are, draw below the circles
70152                 viewfields.enter()
70153                     .insert('path', 'circle')
70154                     .attr('class', 'viewfield')
70155                     .attr('transform', 'scale(1.5,1.5),translate(-8, -13)')
70156                     .attr('d', viewfieldPath);
70157
70158                 function viewfieldPath() {
70159                     var d = this.parentNode.__data__;
70160                     if (d.pano) {
70161                         return 'M 8,13 m -10,0 a 10,10 0 1,0 20,0 a 10,10 0 1,0 -20,0';
70162                     } else {
70163                         return 'M 6,9 C 8,8.4 8,8.4 10,9 L 16,-2 C 12,-5 4,-5 0,-2 z';
70164                     }
70165                 }
70166
70167             }
70168
70169             /**
70170              * drawImages()
70171              * drawImages is the method that is returned (and that runs) every time 'svgStreetside()' is called.
70172              * 'svgStreetside()' is called from index.js
70173              */
70174             function drawImages(selection) {
70175                 var enabled = svgStreetside.enabled;
70176                 var service = getService();
70177
70178                 layer = selection.selectAll('.layer-streetside-images')
70179                     .data(service ? [0] : []);
70180
70181                 layer.exit()
70182                     .remove();
70183
70184                 var layerEnter = layer.enter()
70185                     .append('g')
70186                     .attr('class', 'layer-streetside-images')
70187                     .style('display', enabled ? 'block' : 'none');
70188
70189                 layerEnter
70190                     .append('g')
70191                     .attr('class', 'sequences');
70192
70193                 layerEnter
70194                     .append('g')
70195                     .attr('class', 'markers');
70196
70197                 layer = layerEnter
70198                     .merge(layer);
70199
70200                 if (enabled) {
70201                     if (service && ~~context.map().zoom() >= minZoom) {
70202                         editOn();
70203                         update();
70204                         service.loadBubbles(projection);
70205                     } else {
70206                         editOff();
70207                     }
70208                 }
70209             }
70210
70211
70212             /**
70213              * drawImages.enabled().
70214              */
70215             drawImages.enabled = function(_) {
70216                 if (!arguments.length) { return svgStreetside.enabled; }
70217                 svgStreetside.enabled = _;
70218                 if (svgStreetside.enabled) {
70219                     showLayer();
70220                 } else {
70221                     hideLayer();
70222                 }
70223                 dispatch.call('change');
70224                 return this;
70225             };
70226
70227             /**
70228              * drawImages.supported().
70229              */
70230             drawImages.supported = function() {
70231                 return !!getService();
70232             };
70233
70234             init();
70235
70236             return drawImages;
70237         }
70238
70239         function svgMapillaryImages(projection, context, dispatch) {
70240             var throttledRedraw = throttle(function () { dispatch.call('change'); }, 1000);
70241             var minZoom = 12;
70242             var minMarkerZoom = 16;
70243             var minViewfieldZoom = 18;
70244             var layer = select(null);
70245             var _mapillary;
70246             var viewerCompassAngle;
70247
70248
70249             function init() {
70250                 if (svgMapillaryImages.initialized) { return; }  // run once
70251                 svgMapillaryImages.enabled = false;
70252                 svgMapillaryImages.initialized = true;
70253             }
70254
70255
70256             function getService() {
70257                 if (services.mapillary && !_mapillary) {
70258                     _mapillary = services.mapillary;
70259                     _mapillary.event.on('loadedImages', throttledRedraw);
70260                     _mapillary.event.on('bearingChanged', function(e) {
70261                         viewerCompassAngle = e;
70262
70263                         // avoid updating if the map is currently transformed
70264                         // e.g. during drags or easing.
70265                         if (context.map().isTransformed()) { return; }
70266
70267                         layer.selectAll('.viewfield-group.currentView')
70268                             .filter(function(d) {
70269                                 return d.pano;
70270                             })
70271                             .attr('transform', transform);
70272                     });
70273                 } else if (!services.mapillary && _mapillary) {
70274                     _mapillary = null;
70275                 }
70276
70277                 return _mapillary;
70278             }
70279
70280
70281             function showLayer() {
70282                 var service = getService();
70283                 if (!service) { return; }
70284
70285                 editOn();
70286
70287                 layer
70288                     .style('opacity', 0)
70289                     .transition()
70290                     .duration(250)
70291                     .style('opacity', 1)
70292                     .on('end', function () { dispatch.call('change'); });
70293             }
70294
70295
70296             function hideLayer() {
70297                 throttledRedraw.cancel();
70298
70299                 layer
70300                     .transition()
70301                     .duration(250)
70302                     .style('opacity', 0)
70303                     .on('end', editOff);
70304             }
70305
70306
70307             function editOn() {
70308                 layer.style('display', 'block');
70309             }
70310
70311
70312             function editOff() {
70313                 layer.selectAll('.viewfield-group').remove();
70314                 layer.style('display', 'none');
70315             }
70316
70317
70318             function click(d) {
70319                 var service = getService();
70320                 if (!service) { return; }
70321
70322                 service
70323                     .selectImage(context, d.key)
70324                     .updateViewer(context, d.key)
70325                     .showViewer(context);
70326
70327                 context.map().centerEase(d.loc);
70328             }
70329
70330
70331             function mouseover(d) {
70332                 var service = getService();
70333                 if (service) { service.setStyles(context, d); }
70334             }
70335
70336
70337             function mouseout() {
70338                 var service = getService();
70339                 if (service) { service.setStyles(context, null); }
70340             }
70341
70342
70343             function transform(d) {
70344                 var t = svgPointTransform(projection)(d);
70345                 if (d.pano && viewerCompassAngle !== null && isFinite(viewerCompassAngle)) {
70346                     t += ' rotate(' + Math.floor(viewerCompassAngle) + ',0,0)';
70347                 } else if (d.ca) {
70348                     t += ' rotate(' + Math.floor(d.ca) + ',0,0)';
70349                 }
70350                 return t;
70351             }
70352
70353             context.photos().on('change.mapillary_images', update);
70354
70355             function filterImages(images) {
70356                 var showsPano = context.photos().showsPanoramic();
70357                 var showsFlat = context.photos().showsFlat();
70358                 if (!showsPano || !showsFlat) {
70359                     images = images.filter(function(image) {
70360                         if (image.pano) { return showsPano; }
70361                         return showsFlat;
70362                     });
70363                 }
70364                 return images;
70365             }
70366
70367             function filterSequences(sequences, service) {
70368                 var showsPano = context.photos().showsPanoramic();
70369                 var showsFlat = context.photos().showsFlat();
70370                 if (!showsPano || !showsFlat) {
70371                     sequences = sequences.filter(function(sequence) {
70372                         if (sequence.properties.hasOwnProperty('pano')) {
70373                             if (sequence.properties.pano) { return showsPano; }
70374                             return showsFlat;
70375                         } else {
70376                             // if the sequence doesn't specify pano or not, search its images
70377                             var cProps = sequence.properties.coordinateProperties;
70378                             if (cProps && cProps.image_keys && cProps.image_keys.length > 0) {
70379                                 for (var index in cProps.image_keys) {
70380                                     var imageKey = cProps.image_keys[index];
70381                                     var image = service.cachedImage(imageKey);
70382                                     if (image && image.hasOwnProperty('pano')) {
70383                                         if (image.pano) { return showsPano; }
70384                                         return showsFlat;
70385                                     }
70386                                 }
70387                             }
70388                         }
70389                     });
70390                 }
70391                 return sequences;
70392             }
70393
70394             function update() {
70395
70396                 var z = ~~context.map().zoom();
70397                 var showMarkers = (z >= minMarkerZoom);
70398                 var showViewfields = (z >= minViewfieldZoom);
70399
70400                 var service = getService();
70401                 var selectedKey = service && service.getSelectedImageKey();
70402                 var sequences = (service ? service.sequences(projection) : []);
70403                 var images = (service && showMarkers ? service.images(projection) : []);
70404
70405                 images = filterImages(images);
70406                 sequences = filterSequences(sequences, service);
70407
70408                 var traces = layer.selectAll('.sequences').selectAll('.sequence')
70409                     .data(sequences, function(d) { return d.properties.key; });
70410
70411                 // exit
70412                 traces.exit()
70413                     .remove();
70414
70415                 // enter/update
70416                 traces = traces.enter()
70417                     .append('path')
70418                     .attr('class', 'sequence')
70419                     .merge(traces)
70420                     .attr('d', svgPath(projection).geojson);
70421
70422
70423                 var groups = layer.selectAll('.markers').selectAll('.viewfield-group')
70424                     .data(images, function(d) { return d.key; });
70425
70426                 // exit
70427                 groups.exit()
70428                     .remove();
70429
70430                 // enter
70431                 var groupsEnter = groups.enter()
70432                     .append('g')
70433                     .attr('class', 'viewfield-group')
70434                     .on('mouseenter', mouseover)
70435                     .on('mouseleave', mouseout)
70436                     .on('click', click);
70437
70438                 groupsEnter
70439                     .append('g')
70440                     .attr('class', 'viewfield-scale');
70441
70442                 // update
70443                 var markers = groups
70444                     .merge(groupsEnter)
70445                     .sort(function(a, b) {
70446                         return (a.key === selectedKey) ? 1
70447                             : (b.key === selectedKey) ? -1
70448                             : b.loc[1] - a.loc[1];  // sort Y
70449                     })
70450                     .attr('transform', transform)
70451                     .select('.viewfield-scale');
70452
70453
70454                 markers.selectAll('circle')
70455                     .data([0])
70456                     .enter()
70457                     .append('circle')
70458                     .attr('dx', '0')
70459                     .attr('dy', '0')
70460                     .attr('r', '6');
70461
70462                 var viewfields = markers.selectAll('.viewfield')
70463                     .data(showViewfields ? [0] : []);
70464
70465                 viewfields.exit()
70466                     .remove();
70467
70468                 viewfields.enter()               // viewfields may or may not be drawn...
70469                     .insert('path', 'circle')    // but if they are, draw below the circles
70470                     .attr('class', 'viewfield')
70471                     .classed('pano', function() { return this.parentNode.__data__.pano; })
70472                     .attr('transform', 'scale(1.5,1.5),translate(-8, -13)')
70473                     .attr('d', viewfieldPath);
70474
70475                 function viewfieldPath() {
70476                     var d = this.parentNode.__data__;
70477                     if (d.pano) {
70478                         return 'M 8,13 m -10,0 a 10,10 0 1,0 20,0 a 10,10 0 1,0 -20,0';
70479                     } else {
70480                         return 'M 6,9 C 8,8.4 8,8.4 10,9 L 16,-2 C 12,-5 4,-5 0,-2 z';
70481                     }
70482                 }
70483             }
70484
70485
70486             function drawImages(selection) {
70487                 var enabled = svgMapillaryImages.enabled;
70488                 var service = getService();
70489
70490                 layer = selection.selectAll('.layer-mapillary')
70491                     .data(service ? [0] : []);
70492
70493                 layer.exit()
70494                     .remove();
70495
70496                 var layerEnter = layer.enter()
70497                     .append('g')
70498                     .attr('class', 'layer-mapillary')
70499                     .style('display', enabled ? 'block' : 'none');
70500
70501                 layerEnter
70502                     .append('g')
70503                     .attr('class', 'sequences');
70504
70505                 layerEnter
70506                     .append('g')
70507                     .attr('class', 'markers');
70508
70509                 layer = layerEnter
70510                     .merge(layer);
70511
70512                 if (enabled) {
70513                     if (service && ~~context.map().zoom() >= minZoom) {
70514                         editOn();
70515                         update();
70516                         service.loadImages(projection);
70517                     } else {
70518                         editOff();
70519                     }
70520                 }
70521             }
70522
70523
70524             drawImages.enabled = function(_) {
70525                 if (!arguments.length) { return svgMapillaryImages.enabled; }
70526                 svgMapillaryImages.enabled = _;
70527                 if (svgMapillaryImages.enabled) {
70528                     showLayer();
70529                 } else {
70530                     hideLayer();
70531                 }
70532                 dispatch.call('change');
70533                 return this;
70534             };
70535
70536
70537             drawImages.supported = function() {
70538                 return !!getService();
70539             };
70540
70541
70542             init();
70543             return drawImages;
70544         }
70545
70546         function svgMapillarySigns(projection, context, dispatch) {
70547             var throttledRedraw = throttle(function () { dispatch.call('change'); }, 1000);
70548             var minZoom = 12;
70549             var layer = select(null);
70550             var _mapillary;
70551
70552
70553             function init() {
70554                 if (svgMapillarySigns.initialized) { return; }  // run once
70555                 svgMapillarySigns.enabled = false;
70556                 svgMapillarySigns.initialized = true;
70557             }
70558
70559
70560             function getService() {
70561                 if (services.mapillary && !_mapillary) {
70562                     _mapillary = services.mapillary;
70563                     _mapillary.event.on('loadedSigns', throttledRedraw);
70564                 } else if (!services.mapillary && _mapillary) {
70565                     _mapillary = null;
70566                 }
70567                 return _mapillary;
70568             }
70569
70570
70571             function showLayer() {
70572                 var service = getService();
70573                 if (!service) { return; }
70574
70575                 editOn();
70576             }
70577
70578
70579             function hideLayer() {
70580                 throttledRedraw.cancel();
70581                 editOff();
70582             }
70583
70584
70585             function editOn() {
70586                 layer.style('display', 'block');
70587             }
70588
70589
70590             function editOff() {
70591                 layer.selectAll('.icon-sign').remove();
70592                 layer.style('display', 'none');
70593             }
70594
70595
70596             function click(d) {
70597                 var service = getService();
70598                 if (!service) { return; }
70599
70600                 context.map().centerEase(d.loc);
70601
70602                 var selectedImageKey = service.getSelectedImageKey();
70603                 var imageKey;
70604
70605                 // Pick one of the images the sign was detected in,
70606                 // preference given to an image already selected.
70607                 d.detections.forEach(function(detection) {
70608                     if (!imageKey || selectedImageKey === detection.image_key) {
70609                         imageKey = detection.image_key;
70610                     }
70611                 });
70612
70613                 service
70614                     .selectImage(context, imageKey)
70615                     .updateViewer(context, imageKey)
70616                     .showViewer(context);
70617             }
70618
70619
70620             function update() {
70621                 var service = getService();
70622                 var data = (service ? service.signs(projection) : []);
70623                 var selectedImageKey = service.getSelectedImageKey();
70624                 var transform = svgPointTransform(projection);
70625
70626                 var signs = layer.selectAll('.icon-sign')
70627                     .data(data, function(d) { return d.key; });
70628
70629                 // exit
70630                 signs.exit()
70631                     .remove();
70632
70633                 // enter
70634                 var enter = signs.enter()
70635                     .append('g')
70636                     .attr('class', 'icon-sign icon-detected')
70637                     .on('click', click);
70638
70639                 enter
70640                     .append('use')
70641                     .attr('width', '24px')
70642                     .attr('height', '24px')
70643                     .attr('x', '-12px')
70644                     .attr('y', '-12px')
70645                     .attr('xlink:href', function(d) { return '#' + d.value; });
70646
70647                 enter
70648                     .append('rect')
70649                     .attr('width', '24px')
70650                     .attr('height', '24px')
70651                     .attr('x', '-12px')
70652                     .attr('y', '-12px');
70653
70654                 // update
70655                 signs
70656                     .merge(enter)
70657                     .attr('transform', transform)
70658                     .classed('currentView', function(d) {
70659                         return d.detections.some(function(detection) {
70660                             return detection.image_key === selectedImageKey;
70661                         });
70662                     })
70663                     .sort(function(a, b) {
70664                         var aSelected = a.detections.some(function(detection) {
70665                             return detection.image_key === selectedImageKey;
70666                         });
70667                         var bSelected = b.detections.some(function(detection) {
70668                             return detection.image_key === selectedImageKey;
70669                         });
70670                         if (aSelected === bSelected) {
70671                             return b.loc[1] - a.loc[1]; // sort Y
70672                         } else if (aSelected) {
70673                             return 1;
70674                         }
70675                         return -1;
70676                     });
70677             }
70678
70679
70680             function drawSigns(selection) {
70681                 var enabled = svgMapillarySigns.enabled;
70682                 var service = getService();
70683
70684                 layer = selection.selectAll('.layer-mapillary-signs')
70685                     .data(service ? [0] : []);
70686
70687                 layer.exit()
70688                     .remove();
70689
70690                 layer = layer.enter()
70691                     .append('g')
70692                     .attr('class', 'layer-mapillary-signs layer-mapillary-detections')
70693                     .style('display', enabled ? 'block' : 'none')
70694                     .merge(layer);
70695
70696                 if (enabled) {
70697                     if (service && ~~context.map().zoom() >= minZoom) {
70698                         editOn();
70699                         update();
70700                         service.loadSigns(projection);
70701                     } else {
70702                         editOff();
70703                     }
70704                 }
70705             }
70706
70707
70708             drawSigns.enabled = function(_) {
70709                 if (!arguments.length) { return svgMapillarySigns.enabled; }
70710                 svgMapillarySigns.enabled = _;
70711                 if (svgMapillarySigns.enabled) {
70712                     showLayer();
70713                 } else {
70714                     hideLayer();
70715                 }
70716                 dispatch.call('change');
70717                 return this;
70718             };
70719
70720
70721             drawSigns.supported = function() {
70722                 return !!getService();
70723             };
70724
70725
70726             init();
70727             return drawSigns;
70728         }
70729
70730         function svgMapillaryMapFeatures(projection, context, dispatch) {
70731             var throttledRedraw = throttle(function () { dispatch.call('change'); }, 1000);
70732             var minZoom = 12;
70733             var layer = select(null);
70734             var _mapillary;
70735
70736
70737             function init() {
70738                 if (svgMapillaryMapFeatures.initialized) { return; }  // run once
70739                 svgMapillaryMapFeatures.enabled = false;
70740                 svgMapillaryMapFeatures.initialized = true;
70741             }
70742
70743
70744             function getService() {
70745                 if (services.mapillary && !_mapillary) {
70746                     _mapillary = services.mapillary;
70747                     _mapillary.event.on('loadedMapFeatures', throttledRedraw);
70748                 } else if (!services.mapillary && _mapillary) {
70749                     _mapillary = null;
70750                 }
70751                 return _mapillary;
70752             }
70753
70754
70755             function showLayer() {
70756                 var service = getService();
70757                 if (!service) { return; }
70758
70759                 editOn();
70760             }
70761
70762
70763             function hideLayer() {
70764                 throttledRedraw.cancel();
70765                 editOff();
70766             }
70767
70768
70769             function editOn() {
70770                 layer.style('display', 'block');
70771             }
70772
70773
70774             function editOff() {
70775                 layer.selectAll('.icon-map-feature').remove();
70776                 layer.style('display', 'none');
70777             }
70778
70779
70780             function click(d) {
70781                 var service = getService();
70782                 if (!service) { return; }
70783
70784                 context.map().centerEase(d.loc);
70785
70786                 var selectedImageKey = service.getSelectedImageKey();
70787                 var imageKey;
70788
70789                 // Pick one of the images the map feature was detected in,
70790                 // preference given to an image already selected.
70791                 d.detections.forEach(function(detection) {
70792                     if (!imageKey || selectedImageKey === detection.image_key) {
70793                         imageKey = detection.image_key;
70794                     }
70795                 });
70796
70797                 service
70798                     .selectImage(context, imageKey)
70799                     .updateViewer(context, imageKey)
70800                     .showViewer(context);
70801             }
70802
70803
70804             function update() {
70805                 var service = getService();
70806                 var data = (service ? service.mapFeatures(projection) : []);
70807                 var selectedImageKey = service && service.getSelectedImageKey();
70808                 var transform = svgPointTransform(projection);
70809
70810                 var mapFeatures = layer.selectAll('.icon-map-feature')
70811                     .data(data, function(d) { return d.key; });
70812
70813                 // exit
70814                 mapFeatures.exit()
70815                     .remove();
70816
70817                 // enter
70818                 var enter = mapFeatures.enter()
70819                     .append('g')
70820                     .attr('class', 'icon-map-feature icon-detected')
70821                     .on('click', click);
70822
70823                 enter
70824                     .append('title')
70825                     .text(function(d) {
70826                         var id = d.value.replace(/--/g, '.').replace(/-/g, '_');
70827                         return _t('mapillary_map_features.' + id);
70828                     });
70829
70830                 enter
70831                     .append('use')
70832                     .attr('width', '24px')
70833                     .attr('height', '24px')
70834                     .attr('x', '-12px')
70835                     .attr('y', '-12px')
70836                     .attr('xlink:href', function(d) {
70837                         if (d.value === 'object--billboard') {
70838                             // no billboard icon right now, so use the advertisement icon
70839                             return '#object--sign--advertisement';
70840                         }
70841                         return '#' + d.value;
70842                     });
70843
70844                 enter
70845                     .append('rect')
70846                     .attr('width', '24px')
70847                     .attr('height', '24px')
70848                     .attr('x', '-12px')
70849                     .attr('y', '-12px');
70850
70851                 // update
70852                 mapFeatures
70853                     .merge(enter)
70854                     .attr('transform', transform)
70855                     .classed('currentView', function(d) {
70856                         return d.detections.some(function(detection) {
70857                             return detection.image_key === selectedImageKey;
70858                         });
70859                     })
70860                     .sort(function(a, b) {
70861                         var aSelected = a.detections.some(function(detection) {
70862                             return detection.image_key === selectedImageKey;
70863                         });
70864                         var bSelected = b.detections.some(function(detection) {
70865                             return detection.image_key === selectedImageKey;
70866                         });
70867                         if (aSelected === bSelected) {
70868                             return b.loc[1] - a.loc[1]; // sort Y
70869                         } else if (aSelected) {
70870                             return 1;
70871                         }
70872                         return -1;
70873                     });
70874             }
70875
70876
70877             function drawMapFeatures(selection) {
70878                 var enabled = svgMapillaryMapFeatures.enabled;
70879                 var service = getService();
70880
70881                 layer = selection.selectAll('.layer-mapillary-map-features')
70882                     .data(service ? [0] : []);
70883
70884                 layer.exit()
70885                     .remove();
70886
70887                 layer = layer.enter()
70888                     .append('g')
70889                     .attr('class', 'layer-mapillary-map-features layer-mapillary-detections')
70890                     .style('display', enabled ? 'block' : 'none')
70891                     .merge(layer);
70892
70893                 if (enabled) {
70894                     if (service && ~~context.map().zoom() >= minZoom) {
70895                         editOn();
70896                         update();
70897                         service.loadMapFeatures(projection);
70898                     } else {
70899                         editOff();
70900                     }
70901                 }
70902             }
70903
70904
70905             drawMapFeatures.enabled = function(_) {
70906                 if (!arguments.length) { return svgMapillaryMapFeatures.enabled; }
70907                 svgMapillaryMapFeatures.enabled = _;
70908                 if (svgMapillaryMapFeatures.enabled) {
70909                     showLayer();
70910                 } else {
70911                     hideLayer();
70912                 }
70913                 dispatch.call('change');
70914                 return this;
70915             };
70916
70917
70918             drawMapFeatures.supported = function() {
70919                 return !!getService();
70920             };
70921
70922
70923             init();
70924             return drawMapFeatures;
70925         }
70926
70927         function svgOpenstreetcamImages(projection, context, dispatch) {
70928             var throttledRedraw = throttle(function () { dispatch.call('change'); }, 1000);
70929             var minZoom = 12;
70930             var minMarkerZoom = 16;
70931             var minViewfieldZoom = 18;
70932             var layer = select(null);
70933             var _openstreetcam;
70934
70935
70936             function init() {
70937                 if (svgOpenstreetcamImages.initialized) { return; }  // run once
70938                 svgOpenstreetcamImages.enabled = false;
70939                 svgOpenstreetcamImages.initialized = true;
70940             }
70941
70942
70943             function getService() {
70944                 if (services.openstreetcam && !_openstreetcam) {
70945                     _openstreetcam = services.openstreetcam;
70946                     _openstreetcam.event.on('loadedImages', throttledRedraw);
70947                 } else if (!services.openstreetcam && _openstreetcam) {
70948                     _openstreetcam = null;
70949                 }
70950
70951                 return _openstreetcam;
70952             }
70953
70954
70955             function showLayer() {
70956                 var service = getService();
70957                 if (!service) { return; }
70958
70959                 editOn();
70960
70961                 layer
70962                     .style('opacity', 0)
70963                     .transition()
70964                     .duration(250)
70965                     .style('opacity', 1)
70966                     .on('end', function () { dispatch.call('change'); });
70967             }
70968
70969
70970             function hideLayer() {
70971                 throttledRedraw.cancel();
70972
70973                 layer
70974                     .transition()
70975                     .duration(250)
70976                     .style('opacity', 0)
70977                     .on('end', editOff);
70978             }
70979
70980
70981             function editOn() {
70982                 layer.style('display', 'block');
70983             }
70984
70985
70986             function editOff() {
70987                 layer.selectAll('.viewfield-group').remove();
70988                 layer.style('display', 'none');
70989             }
70990
70991
70992             function click(d) {
70993                 var service = getService();
70994                 if (!service) { return; }
70995
70996                 service
70997                     .selectImage(context, d)
70998                     .updateViewer(context, d)
70999                     .showViewer(context);
71000
71001                 context.map().centerEase(d.loc);
71002             }
71003
71004
71005             function mouseover(d) {
71006                 var service = getService();
71007                 if (service) { service.setStyles(context, d); }
71008             }
71009
71010
71011             function mouseout() {
71012                 var service = getService();
71013                 if (service) { service.setStyles(context, null); }
71014             }
71015
71016
71017             function transform(d) {
71018                 var t = svgPointTransform(projection)(d);
71019                 if (d.ca) {
71020                     t += ' rotate(' + Math.floor(d.ca) + ',0,0)';
71021                 }
71022                 return t;
71023             }
71024
71025
71026             context.photos().on('change.openstreetcam_images', update);
71027
71028             function update() {
71029                 var viewer = context.container().select('.photoviewer');
71030                 var selected = viewer.empty() ? undefined : viewer.datum();
71031
71032                 var z = ~~context.map().zoom();
71033                 var showMarkers = (z >= minMarkerZoom);
71034                 var showViewfields = (z >= minViewfieldZoom);
71035
71036                 var service = getService();
71037                 var sequences = [];
71038                 var images = [];
71039
71040                 if (context.photos().showsFlat()) {
71041                     sequences = (service ? service.sequences(projection) : []);
71042                     images = (service && showMarkers ? service.images(projection) : []);
71043                 }
71044
71045                 var traces = layer.selectAll('.sequences').selectAll('.sequence')
71046                     .data(sequences, function(d) { return d.properties.key; });
71047
71048                 // exit
71049                 traces.exit()
71050                     .remove();
71051
71052                 // enter/update
71053                 traces = traces.enter()
71054                     .append('path')
71055                     .attr('class', 'sequence')
71056                     .merge(traces)
71057                     .attr('d', svgPath(projection).geojson);
71058
71059
71060                 var groups = layer.selectAll('.markers').selectAll('.viewfield-group')
71061                     .data(images, function(d) { return d.key; });
71062
71063                 // exit
71064                 groups.exit()
71065                     .remove();
71066
71067                 // enter
71068                 var groupsEnter = groups.enter()
71069                     .append('g')
71070                     .attr('class', 'viewfield-group')
71071                     .on('mouseenter', mouseover)
71072                     .on('mouseleave', mouseout)
71073                     .on('click', click);
71074
71075                 groupsEnter
71076                     .append('g')
71077                     .attr('class', 'viewfield-scale');
71078
71079                 // update
71080                 var markers = groups
71081                     .merge(groupsEnter)
71082                     .sort(function(a, b) {
71083                         return (a === selected) ? 1
71084                             : (b === selected) ? -1
71085                             : b.loc[1] - a.loc[1];  // sort Y
71086                     })
71087                     .attr('transform', transform)
71088                     .select('.viewfield-scale');
71089
71090
71091                 markers.selectAll('circle')
71092                     .data([0])
71093                     .enter()
71094                     .append('circle')
71095                     .attr('dx', '0')
71096                     .attr('dy', '0')
71097                     .attr('r', '6');
71098
71099                 var viewfields = markers.selectAll('.viewfield')
71100                     .data(showViewfields ? [0] : []);
71101
71102                 viewfields.exit()
71103                     .remove();
71104
71105                 viewfields.enter()               // viewfields may or may not be drawn...
71106                     .insert('path', 'circle')    // but if they are, draw below the circles
71107                     .attr('class', 'viewfield')
71108                     .attr('transform', 'scale(1.5,1.5),translate(-8, -13)')
71109                     .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');
71110             }
71111
71112
71113             function drawImages(selection) {
71114                 var enabled = svgOpenstreetcamImages.enabled,
71115                     service = getService();
71116
71117                 layer = selection.selectAll('.layer-openstreetcam')
71118                     .data(service ? [0] : []);
71119
71120                 layer.exit()
71121                     .remove();
71122
71123                 var layerEnter = layer.enter()
71124                     .append('g')
71125                     .attr('class', 'layer-openstreetcam')
71126                     .style('display', enabled ? 'block' : 'none');
71127
71128                 layerEnter
71129                     .append('g')
71130                     .attr('class', 'sequences');
71131
71132                 layerEnter
71133                     .append('g')
71134                     .attr('class', 'markers');
71135
71136                 layer = layerEnter
71137                     .merge(layer);
71138
71139                 if (enabled) {
71140                     if (service && ~~context.map().zoom() >= minZoom) {
71141                         editOn();
71142                         update();
71143                         service.loadImages(projection);
71144                     } else {
71145                         editOff();
71146                     }
71147                 }
71148             }
71149
71150
71151             drawImages.enabled = function(_) {
71152                 if (!arguments.length) { return svgOpenstreetcamImages.enabled; }
71153                 svgOpenstreetcamImages.enabled = _;
71154                 if (svgOpenstreetcamImages.enabled) {
71155                     showLayer();
71156                 } else {
71157                     hideLayer();
71158                 }
71159                 dispatch.call('change');
71160                 return this;
71161             };
71162
71163
71164             drawImages.supported = function() {
71165                 return !!getService();
71166             };
71167
71168
71169             init();
71170             return drawImages;
71171         }
71172
71173         function svgOsm(projection, context, dispatch) {
71174             var enabled = true;
71175
71176
71177             function drawOsm(selection) {
71178                 selection.selectAll('.layer-osm')
71179                     .data(['covered', 'areas', 'lines', 'points', 'labels'])
71180                     .enter()
71181                     .append('g')
71182                     .attr('class', function(d) { return 'layer-osm ' + d; });
71183
71184                 selection.selectAll('.layer-osm.points').selectAll('.points-group')
71185                     .data(['points', 'midpoints', 'vertices', 'turns'])
71186                     .enter()
71187                     .append('g')
71188                     .attr('class', function(d) { return 'points-group ' + d; });
71189             }
71190
71191
71192             function showLayer() {
71193                 var layer = context.surface().selectAll('.data-layer.osm');
71194                 layer.interrupt();
71195
71196                 layer
71197                     .classed('disabled', false)
71198                     .style('opacity', 0)
71199                     .transition()
71200                     .duration(250)
71201                     .style('opacity', 1)
71202                     .on('end interrupt', function () {
71203                         dispatch.call('change');
71204                     });
71205             }
71206
71207
71208             function hideLayer() {
71209                 var layer = context.surface().selectAll('.data-layer.osm');
71210                 layer.interrupt();
71211
71212                 layer
71213                     .transition()
71214                     .duration(250)
71215                     .style('opacity', 0)
71216                     .on('end interrupt', function () {
71217                         layer.classed('disabled', true);
71218                         dispatch.call('change');
71219                     });
71220             }
71221
71222
71223             drawOsm.enabled = function(val) {
71224                 if (!arguments.length) { return enabled; }
71225                 enabled = val;
71226
71227                 if (enabled) {
71228                     showLayer();
71229                 } else {
71230                     hideLayer();
71231                 }
71232
71233                 dispatch.call('change');
71234                 return this;
71235             };
71236
71237
71238             return drawOsm;
71239         }
71240
71241         var _notesEnabled = false;
71242         var _osmService;
71243
71244
71245         function svgNotes(projection, context, dispatch$1) {
71246             if (!dispatch$1) { dispatch$1 = dispatch('change'); }
71247             var throttledRedraw = throttle(function () { dispatch$1.call('change'); }, 1000);
71248             var minZoom = 12;
71249             var touchLayer = select(null);
71250             var drawLayer = select(null);
71251             var _notesVisible = false;
71252
71253
71254             function markerPath(selection, klass) {
71255                 selection
71256                     .attr('class', klass)
71257                     .attr('transform', 'translate(-8, -22)')
71258                     .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');
71259             }
71260
71261
71262             // Loosely-coupled osm service for fetching notes.
71263             function getService() {
71264                 if (services.osm && !_osmService) {
71265                     _osmService = services.osm;
71266                     _osmService.on('loadedNotes', throttledRedraw);
71267                 } else if (!services.osm && _osmService) {
71268                     _osmService = null;
71269                 }
71270
71271                 return _osmService;
71272             }
71273
71274
71275             // Show the notes
71276             function editOn() {
71277                 if (!_notesVisible) {
71278                     _notesVisible = true;
71279                     drawLayer
71280                         .style('display', 'block');
71281                 }
71282             }
71283
71284
71285             // Immediately remove the notes and their touch targets
71286             function editOff() {
71287                 if (_notesVisible) {
71288                     _notesVisible = false;
71289                     drawLayer
71290                         .style('display', 'none');
71291                     drawLayer.selectAll('.note')
71292                         .remove();
71293                     touchLayer.selectAll('.note')
71294                         .remove();
71295                 }
71296             }
71297
71298
71299             // Enable the layer.  This shows the notes and transitions them to visible.
71300             function layerOn() {
71301                 editOn();
71302
71303                 drawLayer
71304                     .style('opacity', 0)
71305                     .transition()
71306                     .duration(250)
71307                     .style('opacity', 1)
71308                     .on('end interrupt', function () {
71309                         dispatch$1.call('change');
71310                     });
71311             }
71312
71313
71314             // Disable the layer.  This transitions the layer invisible and then hides the notes.
71315             function layerOff() {
71316                 throttledRedraw.cancel();
71317                 drawLayer.interrupt();
71318                 touchLayer.selectAll('.note')
71319                     .remove();
71320
71321                 drawLayer
71322                     .transition()
71323                     .duration(250)
71324                     .style('opacity', 0)
71325                     .on('end interrupt', function () {
71326                         editOff();
71327                         dispatch$1.call('change');
71328                     });
71329             }
71330
71331
71332             // Update the note markers
71333             function updateMarkers() {
71334                 if (!_notesVisible || !_notesEnabled) { return; }
71335
71336                 var service = getService();
71337                 var selectedID = context.selectedNoteID();
71338                 var data = (service ? service.notes(projection) : []);
71339                 var getTransform = svgPointTransform(projection);
71340
71341                 // Draw markers..
71342                 var notes = drawLayer.selectAll('.note')
71343                     .data(data, function(d) { return d.status + d.id; });
71344
71345                 // exit
71346                 notes.exit()
71347                     .remove();
71348
71349                 // enter
71350                 var notesEnter = notes.enter()
71351                     .append('g')
71352                     .attr('class', function(d) { return 'note note-' + d.id + ' ' + d.status; })
71353                     .classed('new', function(d) { return d.id < 0; });
71354
71355                 notesEnter
71356                     .append('ellipse')
71357                     .attr('cx', 0.5)
71358                     .attr('cy', 1)
71359                     .attr('rx', 6.5)
71360                     .attr('ry', 3)
71361                     .attr('class', 'stroke');
71362
71363                 notesEnter
71364                     .append('path')
71365                     .call(markerPath, 'shadow');
71366
71367                 notesEnter
71368                     .append('use')
71369                     .attr('class', 'note-fill')
71370                     .attr('width', '20px')
71371                     .attr('height', '20px')
71372                     .attr('x', '-8px')
71373                     .attr('y', '-22px')
71374                     .attr('xlink:href', '#iD-icon-note');
71375
71376                 notesEnter.selectAll('.icon-annotation')
71377                     .data(function(d) { return [d]; })
71378                     .enter()
71379                     .append('use')
71380                     .attr('class', 'icon-annotation')
71381                     .attr('width', '10px')
71382                     .attr('height', '10px')
71383                     .attr('x', '-3px')
71384                     .attr('y', '-19px')
71385                     .attr('xlink:href', function(d) {
71386                         return '#iD-icon-' + (d.id < 0 ? 'plus' : (d.status === 'open' ? 'close' : 'apply'));
71387                     });
71388
71389                 // update
71390                 notes
71391                     .merge(notesEnter)
71392                     .sort(sortY)
71393                     .classed('selected', function(d) {
71394                         var mode = context.mode();
71395                         var isMoving = mode && mode.id === 'drag-note';  // no shadows when dragging
71396                         return !isMoving && d.id === selectedID;
71397                     })
71398                     .attr('transform', getTransform);
71399
71400
71401                 // Draw targets..
71402                 if (touchLayer.empty()) { return; }
71403                 var fillClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
71404
71405                 var targets = touchLayer.selectAll('.note')
71406                     .data(data, function(d) { return d.id; });
71407
71408                 // exit
71409                 targets.exit()
71410                     .remove();
71411
71412                 // enter/update
71413                 targets.enter()
71414                     .append('rect')
71415                     .attr('width', '20px')
71416                     .attr('height', '20px')
71417                     .attr('x', '-8px')
71418                     .attr('y', '-22px')
71419                     .merge(targets)
71420                     .sort(sortY)
71421                     .attr('class', function(d) {
71422                         var newClass = (d.id < 0 ? 'new' : '');
71423                         return 'note target note-' + d.id + ' ' + fillClass + newClass;
71424                     })
71425                     .attr('transform', getTransform);
71426
71427
71428                 function sortY(a, b) {
71429                     return (a.id === selectedID) ? 1 : (b.id === selectedID) ? -1 : b.loc[1] - a.loc[1];
71430                 }
71431             }
71432
71433
71434             // Draw the notes layer and schedule loading notes and updating markers.
71435             function drawNotes(selection) {
71436                 var service = getService();
71437
71438                 var surface = context.surface();
71439                 if (surface && !surface.empty()) {
71440                     touchLayer = surface.selectAll('.data-layer.touch .layer-touch.markers');
71441                 }
71442
71443                 drawLayer = selection.selectAll('.layer-notes')
71444                     .data(service ? [0] : []);
71445
71446                 drawLayer.exit()
71447                     .remove();
71448
71449                 drawLayer = drawLayer.enter()
71450                     .append('g')
71451                     .attr('class', 'layer-notes')
71452                     .style('display', _notesEnabled ? 'block' : 'none')
71453                     .merge(drawLayer);
71454
71455                 if (_notesEnabled) {
71456                     if (service && ~~context.map().zoom() >= minZoom) {
71457                         editOn();
71458                         service.loadNotes(projection);
71459                         updateMarkers();
71460                     } else {
71461                         editOff();
71462                     }
71463                 }
71464             }
71465
71466
71467             // Toggles the layer on and off
71468             drawNotes.enabled = function(val) {
71469                 if (!arguments.length) { return _notesEnabled; }
71470
71471                 _notesEnabled = val;
71472                 if (_notesEnabled) {
71473                     layerOn();
71474                 } else {
71475                     layerOff();
71476                     if (context.selectedNoteID()) {
71477                         context.enter(modeBrowse(context));
71478                     }
71479                 }
71480
71481                 dispatch$1.call('change');
71482                 return this;
71483             };
71484
71485
71486             return drawNotes;
71487         }
71488
71489         function svgTouch() {
71490
71491             function drawTouch(selection) {
71492                 selection.selectAll('.layer-touch')
71493                     .data(['areas', 'lines', 'points', 'turns', 'markers'])
71494                     .enter()
71495                     .append('g')
71496                     .attr('class', function(d) { return 'layer-touch ' + d; });
71497             }
71498
71499             return drawTouch;
71500         }
71501
71502         function refresh(selection, node) {
71503             var cr = node.getBoundingClientRect();
71504             var prop = [cr.width, cr.height];
71505             selection.property('__dimensions__', prop);
71506             return prop;
71507         }
71508
71509         function utilGetDimensions(selection, force) {
71510             if (!selection || selection.empty()) {
71511                 return [0, 0];
71512             }
71513             var node = selection.node(),
71514                 cached = selection.property('__dimensions__');
71515             return (!cached || force) ? refresh(selection, node) : cached;
71516         }
71517
71518
71519         function utilSetDimensions(selection, dimensions) {
71520             if (!selection || selection.empty()) {
71521                 return selection;
71522             }
71523             var node = selection.node();
71524             if (dimensions === null) {
71525                 refresh(selection, node);
71526                 return selection;
71527             }
71528             return selection
71529                 .property('__dimensions__', [dimensions[0], dimensions[1]])
71530                 .attr('width', dimensions[0])
71531                 .attr('height', dimensions[1]);
71532         }
71533
71534         function svgLayers(projection, context) {
71535             var dispatch$1 = dispatch('change');
71536             var svg = select(null);
71537             var _layers = [
71538                 { id: 'osm', layer: svgOsm(projection, context, dispatch$1) },
71539                 { id: 'notes', layer: svgNotes(projection, context, dispatch$1) },
71540                 { id: 'data', layer: svgData(projection, context, dispatch$1) },
71541                 { id: 'keepRight', layer: svgKeepRight(projection, context, dispatch$1) },
71542                 { id: 'improveOSM', layer: svgImproveOSM(projection, context, dispatch$1) },
71543                 { id: 'osmose', layer: svgOsmose(projection, context, dispatch$1) },
71544                 { id: 'streetside', layer: svgStreetside(projection, context, dispatch$1)},
71545                 { id: 'mapillary', layer: svgMapillaryImages(projection, context, dispatch$1) },
71546                 { id: 'mapillary-map-features',  layer: svgMapillaryMapFeatures(projection, context, dispatch$1) },
71547                 { id: 'mapillary-signs',  layer: svgMapillarySigns(projection, context, dispatch$1) },
71548                 { id: 'openstreetcam', layer: svgOpenstreetcamImages(projection, context, dispatch$1) },
71549                 { id: 'debug', layer: svgDebug(projection, context) },
71550                 { id: 'geolocate', layer: svgGeolocate(projection) },
71551                 { id: 'touch', layer: svgTouch() }
71552             ];
71553
71554
71555             function drawLayers(selection) {
71556                 svg = selection.selectAll('.surface')
71557                     .data([0]);
71558
71559                 svg = svg.enter()
71560                     .append('svg')
71561                     .attr('class', 'surface')
71562                     .merge(svg);
71563
71564                 var defs = svg.selectAll('.surface-defs')
71565                     .data([0]);
71566
71567                 defs.enter()
71568                     .append('defs')
71569                     .attr('class', 'surface-defs');
71570
71571                 var groups = svg.selectAll('.data-layer')
71572                     .data(_layers);
71573
71574                 groups.exit()
71575                     .remove();
71576
71577                 groups.enter()
71578                     .append('g')
71579                     .attr('class', function(d) { return 'data-layer ' + d.id; })
71580                     .merge(groups)
71581                     .each(function(d) { select(this).call(d.layer); });
71582             }
71583
71584
71585             drawLayers.all = function() {
71586                 return _layers;
71587             };
71588
71589
71590             drawLayers.layer = function(id) {
71591                 var obj = _layers.find(function(o) { return o.id === id; });
71592                 return obj && obj.layer;
71593             };
71594
71595
71596             drawLayers.only = function(what) {
71597                 var arr = [].concat(what);
71598                 var all = _layers.map(function(layer) { return layer.id; });
71599                 return drawLayers.remove(utilArrayDifference(all, arr));
71600             };
71601
71602
71603             drawLayers.remove = function(what) {
71604                 var arr = [].concat(what);
71605                 arr.forEach(function(id) {
71606                     _layers = _layers.filter(function(o) { return o.id !== id; });
71607                 });
71608                 dispatch$1.call('change');
71609                 return this;
71610             };
71611
71612
71613             drawLayers.add = function(what) {
71614                 var arr = [].concat(what);
71615                 arr.forEach(function(obj) {
71616                     if ('id' in obj && 'layer' in obj) {
71617                         _layers.push(obj);
71618                     }
71619                 });
71620                 dispatch$1.call('change');
71621                 return this;
71622             };
71623
71624
71625             drawLayers.dimensions = function(val) {
71626                 if (!arguments.length) { return utilGetDimensions(svg); }
71627                 utilSetDimensions(svg, val);
71628                 return this;
71629             };
71630
71631
71632             return utilRebind(drawLayers, dispatch$1, 'on');
71633         }
71634
71635         function svgLines(projection, context) {
71636             var detected = utilDetect();
71637
71638             var highway_stack = {
71639                 motorway: 0,
71640                 motorway_link: 1,
71641                 trunk: 2,
71642                 trunk_link: 3,
71643                 primary: 4,
71644                 primary_link: 5,
71645                 secondary: 6,
71646                 tertiary: 7,
71647                 unclassified: 8,
71648                 residential: 9,
71649                 service: 10,
71650                 footway: 11
71651             };
71652
71653
71654             function drawTargets(selection, graph, entities, filter) {
71655                 var targetClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
71656                 var nopeClass = context.getDebug('target') ? 'red ' : 'nocolor ';
71657                 var getPath = svgPath(projection).geojson;
71658                 var activeID = context.activeID();
71659                 var base = context.history().base();
71660
71661                 // The targets and nopes will be MultiLineString sub-segments of the ways
71662                 var data = { targets: [], nopes: [] };
71663
71664                 entities.forEach(function(way) {
71665                     var features = svgSegmentWay(way, graph, activeID);
71666                     data.targets.push.apply(data.targets, features.passive);
71667                     data.nopes.push.apply(data.nopes, features.active);
71668                 });
71669
71670
71671                 // Targets allow hover and vertex snapping
71672                 var targetData = data.targets.filter(getPath);
71673                 var targets = selection.selectAll('.line.target-allowed')
71674                     .filter(function(d) { return filter(d.properties.entity); })
71675                     .data(targetData, function key(d) { return d.id; });
71676
71677                 // exit
71678                 targets.exit()
71679                     .remove();
71680
71681                 var segmentWasEdited = function(d) {
71682                     var wayID = d.properties.entity.id;
71683                     // if the whole line was edited, don't draw segment changes
71684                     if (!base.entities[wayID] ||
71685                         !fastDeepEqual(graph.entities[wayID].nodes, base.entities[wayID].nodes)) {
71686                         return false;
71687                     }
71688                     return d.properties.nodes.some(function(n) {
71689                         return !base.entities[n.id] ||
71690                                !fastDeepEqual(graph.entities[n.id].loc, base.entities[n.id].loc);
71691                     });
71692                 };
71693
71694                 // enter/update
71695                 targets.enter()
71696                     .append('path')
71697                     .merge(targets)
71698                     .attr('d', getPath)
71699                     .attr('class', function(d) {
71700                         return 'way line target target-allowed ' + targetClass + d.id;
71701                     })
71702                     .classed('segment-edited', segmentWasEdited);
71703
71704                 // NOPE
71705                 var nopeData = data.nopes.filter(getPath);
71706                 var nopes = selection.selectAll('.line.target-nope')
71707                     .filter(function(d) { return filter(d.properties.entity); })
71708                     .data(nopeData, function key(d) { return d.id; });
71709
71710                 // exit
71711                 nopes.exit()
71712                     .remove();
71713
71714                 // enter/update
71715                 nopes.enter()
71716                     .append('path')
71717                     .merge(nopes)
71718                     .attr('d', getPath)
71719                     .attr('class', function(d) {
71720                         return 'way line target target-nope ' + nopeClass + d.id;
71721                     })
71722                     .classed('segment-edited', segmentWasEdited);
71723             }
71724
71725
71726             function drawLines(selection, graph, entities, filter) {
71727                 var base = context.history().base();
71728
71729                 function waystack(a, b) {
71730                     var selected = context.selectedIDs();
71731                     var scoreA = selected.indexOf(a.id) !== -1 ? 20 : 0;
71732                     var scoreB = selected.indexOf(b.id) !== -1 ? 20 : 0;
71733
71734                     if (a.tags.highway) { scoreA -= highway_stack[a.tags.highway]; }
71735                     if (b.tags.highway) { scoreB -= highway_stack[b.tags.highway]; }
71736                     return scoreA - scoreB;
71737                 }
71738
71739
71740                 function drawLineGroup(selection, klass, isSelected) {
71741                     // Note: Don't add `.selected` class in draw modes
71742                     var mode = context.mode();
71743                     var isDrawing = mode && /^draw/.test(mode.id);
71744                     var selectedClass = (!isDrawing && isSelected) ? 'selected ' : '';
71745
71746                     var lines = selection
71747                         .selectAll('path')
71748                         .filter(filter)
71749                         .data(getPathData(isSelected), osmEntity.key);
71750
71751                     lines.exit()
71752                         .remove();
71753
71754                     // Optimization: Call expensive TagClasses only on enter selection. This
71755                     // works because osmEntity.key is defined to include the entity v attribute.
71756                     lines.enter()
71757                         .append('path')
71758                         .attr('class', function(d) {
71759
71760                             var prefix = 'way line';
71761
71762                             // if this line isn't styled by its own tags
71763                             if (!d.hasInterestingTags()) {
71764
71765                                 var parentRelations = graph.parentRelations(d);
71766                                 var parentMultipolygons = parentRelations.filter(function(relation) {
71767                                     return relation.isMultipolygon();
71768                                 });
71769
71770                                 // and if it's a member of at least one multipolygon relation
71771                                 if (parentMultipolygons.length > 0 &&
71772                                     // and only multipolygon relations
71773                                     parentRelations.length === parentMultipolygons.length) {
71774                                     // then fudge the classes to style this as an area edge
71775                                     prefix = 'relation area';
71776                                 }
71777                             }
71778
71779                             var oldMPClass = oldMultiPolygonOuters[d.id] ? 'old-multipolygon ' : '';
71780                             return prefix + ' ' + klass + ' ' + selectedClass + oldMPClass + d.id;
71781                         })
71782                         .classed('added', function(d) {
71783                             return !base.entities[d.id];
71784                         })
71785                         .classed('geometry-edited', function(d) {
71786                             return graph.entities[d.id] &&
71787                                 base.entities[d.id] &&
71788                                 !fastDeepEqual(graph.entities[d.id].nodes, base.entities[d.id].nodes);
71789                         })
71790                         .classed('retagged', function(d) {
71791                             return graph.entities[d.id] &&
71792                                 base.entities[d.id] &&
71793                                 !fastDeepEqual(graph.entities[d.id].tags, base.entities[d.id].tags);
71794                         })
71795                         .call(svgTagClasses())
71796                         .merge(lines)
71797                         .sort(waystack)
71798                         .attr('d', getPath)
71799                         .call(svgTagClasses().tags(svgRelationMemberTags(graph)));
71800
71801                     return selection;
71802                 }
71803
71804
71805                 function getPathData(isSelected) {
71806                     return function() {
71807                         var layer = this.parentNode.__data__;
71808                         var data = pathdata[layer] || [];
71809                         return data.filter(function(d) {
71810                             if (isSelected)
71811                                 { return context.selectedIDs().indexOf(d.id) !== -1; }
71812                             else
71813                                 { return context.selectedIDs().indexOf(d.id) === -1; }
71814                         });
71815                     };
71816                 }
71817
71818                 function addMarkers(layergroup, pathclass, groupclass, groupdata, marker) {
71819                     var markergroup = layergroup
71820                         .selectAll('g.' + groupclass)
71821                         .data([pathclass]);
71822
71823                     markergroup = markergroup.enter()
71824                         .append('g')
71825                         .attr('class', groupclass)
71826                         .merge(markergroup);
71827
71828                     var markers = markergroup
71829                         .selectAll('path')
71830                         .filter(filter)
71831                         .data(
71832                             function data() { return groupdata[this.parentNode.__data__] || []; },
71833                             function key(d) { return [d.id, d.index]; }
71834                         );
71835
71836                     markers.exit()
71837                         .remove();
71838
71839                     markers = markers.enter()
71840                         .append('path')
71841                         .attr('class', pathclass)
71842                         .merge(markers)
71843                         .attr('marker-mid', marker)
71844                         .attr('d', function(d) { return d.d; });
71845
71846                     if (detected.ie) {
71847                         markers.each(function() { this.parentNode.insertBefore(this, this); });
71848                     }
71849                 }
71850
71851
71852                 var getPath = svgPath(projection, graph);
71853                 var ways = [];
71854                 var onewaydata = {};
71855                 var sideddata = {};
71856                 var oldMultiPolygonOuters = {};
71857
71858                 for (var i = 0; i < entities.length; i++) {
71859                     var entity = entities[i];
71860                     var outer = osmOldMultipolygonOuterMember(entity, graph);
71861                     if (outer) {
71862                         ways.push(entity.mergeTags(outer.tags));
71863                         oldMultiPolygonOuters[outer.id] = true;
71864                     } else if (entity.geometry(graph) === 'line') {
71865                         ways.push(entity);
71866                     }
71867                 }
71868
71869                 ways = ways.filter(getPath);
71870                 var pathdata = utilArrayGroupBy(ways, function(way) { return way.layer(); });
71871
71872                 Object.keys(pathdata).forEach(function(k) {
71873                     var v = pathdata[k];
71874                     var onewayArr = v.filter(function(d) { return d.isOneWay(); });
71875                     var onewaySegments = svgMarkerSegments(
71876                         projection, graph, 35,
71877                         function shouldReverse(entity) { return entity.tags.oneway === '-1'; },
71878                         function bothDirections(entity) {
71879                             return entity.tags.oneway === 'reversible' || entity.tags.oneway === 'alternating';
71880                         }
71881                     );
71882                     onewaydata[k] = utilArrayFlatten(onewayArr.map(onewaySegments));
71883
71884                     var sidedArr = v.filter(function(d) { return d.isSided(); });
71885                     var sidedSegments = svgMarkerSegments(
71886                         projection, graph, 30,
71887                         function shouldReverse() { return false; },
71888                         function bothDirections() { return false; }
71889                     );
71890                     sideddata[k] = utilArrayFlatten(sidedArr.map(sidedSegments));
71891                 });
71892
71893
71894                 var covered = selection.selectAll('.layer-osm.covered');     // under areas
71895                 var uncovered = selection.selectAll('.layer-osm.lines');     // over areas
71896                 var touchLayer = selection.selectAll('.layer-touch.lines');
71897
71898                 // Draw lines..
71899                 [covered, uncovered].forEach(function(selection) {
71900                     var range = (selection === covered ? range$1(-10,0) : range$1(0,11));
71901                     var layergroup = selection
71902                         .selectAll('g.layergroup')
71903                         .data(range);
71904
71905                     layergroup = layergroup.enter()
71906                         .append('g')
71907                         .attr('class', function(d) { return 'layergroup layer' + String(d); })
71908                         .merge(layergroup);
71909
71910                     layergroup
71911                         .selectAll('g.linegroup')
71912                         .data(['shadow', 'casing', 'stroke', 'shadow-highlighted', 'casing-highlighted', 'stroke-highlighted'])
71913                         .enter()
71914                         .append('g')
71915                         .attr('class', function(d) { return 'linegroup line-' + d; });
71916
71917                     layergroup.selectAll('g.line-shadow')
71918                         .call(drawLineGroup, 'shadow', false);
71919                     layergroup.selectAll('g.line-casing')
71920                         .call(drawLineGroup, 'casing', false);
71921                     layergroup.selectAll('g.line-stroke')
71922                         .call(drawLineGroup, 'stroke', false);
71923
71924                     layergroup.selectAll('g.line-shadow-highlighted')
71925                         .call(drawLineGroup, 'shadow', true);
71926                     layergroup.selectAll('g.line-casing-highlighted')
71927                         .call(drawLineGroup, 'casing', true);
71928                     layergroup.selectAll('g.line-stroke-highlighted')
71929                         .call(drawLineGroup, 'stroke', true);
71930
71931                     addMarkers(layergroup, 'oneway', 'onewaygroup', onewaydata, 'url(#ideditor-oneway-marker)');
71932                     addMarkers(layergroup, 'sided', 'sidedgroup', sideddata,
71933                         function marker(d) {
71934                             var category = graph.entity(d.id).sidednessIdentifier();
71935                             return 'url(#ideditor-sided-marker-' + category + ')';
71936                         }
71937                     );
71938                 });
71939
71940                 // Draw touch targets..
71941                 touchLayer
71942                     .call(drawTargets, graph, ways, filter);
71943             }
71944
71945
71946             return drawLines;
71947         }
71948
71949         function svgMidpoints(projection, context) {
71950             var targetRadius = 8;
71951
71952             function drawTargets(selection, graph, entities, filter) {
71953                 var fillClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
71954                 var getTransform = svgPointTransform(projection).geojson;
71955
71956                 var data = entities.map(function(midpoint) {
71957                     return {
71958                         type: 'Feature',
71959                         id: midpoint.id,
71960                         properties: {
71961                             target: true,
71962                             entity: midpoint
71963                         },
71964                         geometry: {
71965                             type: 'Point',
71966                             coordinates: midpoint.loc
71967                         }
71968                     };
71969                 });
71970
71971                 var targets = selection.selectAll('.midpoint.target')
71972                     .filter(function(d) { return filter(d.properties.entity); })
71973                     .data(data, function key(d) { return d.id; });
71974
71975                 // exit
71976                 targets.exit()
71977                     .remove();
71978
71979                 // enter/update
71980                 targets.enter()
71981                     .append('circle')
71982                     .attr('r', targetRadius)
71983                     .merge(targets)
71984                     .attr('class', function(d) { return 'node midpoint target ' + fillClass + d.id; })
71985                     .attr('transform', getTransform);
71986             }
71987
71988
71989             function drawMidpoints(selection, graph, entities, filter, extent) {
71990                 var drawLayer = selection.selectAll('.layer-osm.points .points-group.midpoints');
71991                 var touchLayer = selection.selectAll('.layer-touch.points');
71992
71993                 var mode = context.mode();
71994                 if ((mode && mode.id !== 'select') || !context.map().withinEditableZoom()) {
71995                     drawLayer.selectAll('.midpoint').remove();
71996                     touchLayer.selectAll('.midpoint.target').remove();
71997                     return;
71998                 }
71999
72000                 var poly = extent.polygon();
72001                 var midpoints = {};
72002
72003                 for (var i = 0; i < entities.length; i++) {
72004                     var entity = entities[i];
72005
72006                     if (entity.type !== 'way') { continue; }
72007                     if (!filter(entity)) { continue; }
72008                     if (context.selectedIDs().indexOf(entity.id) < 0) { continue; }
72009
72010                     var nodes = graph.childNodes(entity);
72011                     for (var j = 0; j < nodes.length - 1; j++) {
72012                         var a = nodes[j];
72013                         var b = nodes[j + 1];
72014                         var id = [a.id, b.id].sort().join('-');
72015
72016                         if (midpoints[id]) {
72017                             midpoints[id].parents.push(entity);
72018                         } else if (geoVecLength(projection(a.loc), projection(b.loc)) > 40) {
72019                             var point = geoVecInterp(a.loc, b.loc, 0.5);
72020                             var loc = null;
72021
72022                             if (extent.intersects(point)) {
72023                                 loc = point;
72024                             } else {
72025                                 for (var k = 0; k < 4; k++) {
72026                                     point = geoLineIntersection([a.loc, b.loc], [poly[k], poly[k + 1]]);
72027                                     if (point &&
72028                                         geoVecLength(projection(a.loc), projection(point)) > 20 &&
72029                                         geoVecLength(projection(b.loc), projection(point)) > 20)
72030                                     {
72031                                         loc = point;
72032                                         break;
72033                                     }
72034                                 }
72035                             }
72036
72037                             if (loc) {
72038                                 midpoints[id] = {
72039                                     type: 'midpoint',
72040                                     id: id,
72041                                     loc: loc,
72042                                     edge: [a.id, b.id],
72043                                     parents: [entity]
72044                                 };
72045                             }
72046                         }
72047                     }
72048                 }
72049
72050
72051                 function midpointFilter(d) {
72052                     if (midpoints[d.id])
72053                         { return true; }
72054
72055                     for (var i = 0; i < d.parents.length; i++) {
72056                         if (filter(d.parents[i])) {
72057                             return true;
72058                         }
72059                     }
72060
72061                     return false;
72062                 }
72063
72064
72065                 var groups = drawLayer.selectAll('.midpoint')
72066                     .filter(midpointFilter)
72067                     .data(Object.values(midpoints), function(d) { return d.id; });
72068
72069                 groups.exit()
72070                     .remove();
72071
72072                 var enter = groups.enter()
72073                     .insert('g', ':first-child')
72074                     .attr('class', 'midpoint');
72075
72076                 enter
72077                     .append('polygon')
72078                     .attr('points', '-6,8 10,0 -6,-8')
72079                     .attr('class', 'shadow');
72080
72081                 enter
72082                     .append('polygon')
72083                     .attr('points', '-3,4 5,0 -3,-4')
72084                     .attr('class', 'fill');
72085
72086                 groups = groups
72087                     .merge(enter)
72088                     .attr('transform', function(d) {
72089                         var translate = svgPointTransform(projection);
72090                         var a = graph.entity(d.edge[0]);
72091                         var b = graph.entity(d.edge[1]);
72092                         var angle = geoAngle(a, b, projection) * (180 / Math.PI);
72093                         return translate(d) + ' rotate(' + angle + ')';
72094                     })
72095                     .call(svgTagClasses().tags(
72096                         function(d) { return d.parents[0].tags; }
72097                     ));
72098
72099                 // Propagate data bindings.
72100                 groups.select('polygon.shadow');
72101                 groups.select('polygon.fill');
72102
72103
72104                 // Draw touch targets..
72105                 touchLayer
72106                     .call(drawTargets, graph, Object.values(midpoints), midpointFilter);
72107             }
72108
72109             return drawMidpoints;
72110         }
72111
72112         function svgPoints(projection, context) {
72113
72114             function markerPath(selection, klass) {
72115                 selection
72116                     .attr('class', klass)
72117                     .attr('transform', 'translate(-8, -23)')
72118                     .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');
72119             }
72120
72121             function sortY(a, b) {
72122                 return b.loc[1] - a.loc[1];
72123             }
72124
72125
72126             // Avoid exit/enter if we're just moving stuff around.
72127             // The node will get a new version but we only need to run the update selection.
72128             function fastEntityKey(d) {
72129                 var mode = context.mode();
72130                 var isMoving = mode && /^(add|draw|drag|move|rotate)/.test(mode.id);
72131                 return isMoving ? d.id : osmEntity.key(d);
72132             }
72133
72134
72135             function drawTargets(selection, graph, entities, filter) {
72136                 var fillClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
72137                 var getTransform = svgPointTransform(projection).geojson;
72138                 var activeID = context.activeID();
72139                 var data = [];
72140
72141                 entities.forEach(function(node) {
72142                     if (activeID === node.id) { return; }   // draw no target on the activeID
72143
72144                     data.push({
72145                         type: 'Feature',
72146                         id: node.id,
72147                         properties: {
72148                             target: true,
72149                             entity: node
72150                         },
72151                         geometry: node.asGeoJSON()
72152                     });
72153                 });
72154
72155                 var targets = selection.selectAll('.point.target')
72156                     .filter(function(d) { return filter(d.properties.entity); })
72157                     .data(data, function key(d) { return d.id; });
72158
72159                 // exit
72160                 targets.exit()
72161                     .remove();
72162
72163                 // enter/update
72164                 targets.enter()
72165                     .append('rect')
72166                     .attr('x', -10)
72167                     .attr('y', -26)
72168                     .attr('width', 20)
72169                     .attr('height', 30)
72170                     .merge(targets)
72171                     .attr('class', function(d) { return 'node point target ' + fillClass + d.id; })
72172                     .attr('transform', getTransform);
72173             }
72174
72175
72176             function drawPoints(selection, graph, entities, filter) {
72177                 var wireframe = context.surface().classed('fill-wireframe');
72178                 var zoom = geoScaleToZoom(projection.scale());
72179                 var base = context.history().base();
72180
72181                 // Points with a direction will render as vertices at higher zooms..
72182                 function renderAsPoint(entity) {
72183                     return entity.geometry(graph) === 'point' &&
72184                         !(zoom >= 18 && entity.directions(graph, projection).length);
72185                 }
72186
72187                 // All points will render as vertices in wireframe mode too..
72188                 var points = wireframe ? [] : entities.filter(renderAsPoint);
72189                 points.sort(sortY);
72190
72191
72192                 var drawLayer = selection.selectAll('.layer-osm.points .points-group.points');
72193                 var touchLayer = selection.selectAll('.layer-touch.points');
72194
72195                 // Draw points..
72196                 var groups = drawLayer.selectAll('g.point')
72197                     .filter(filter)
72198                     .data(points, fastEntityKey);
72199
72200                 groups.exit()
72201                     .remove();
72202
72203                 var enter = groups.enter()
72204                     .append('g')
72205                     .attr('class', function(d) { return 'node point ' + d.id; })
72206                     .order();
72207
72208                 enter
72209                     .append('path')
72210                     .call(markerPath, 'shadow');
72211
72212                 enter
72213                     .append('ellipse')
72214                     .attr('cx', 0.5)
72215                     .attr('cy', 1)
72216                     .attr('rx', 6.5)
72217                     .attr('ry', 3)
72218                     .attr('class', 'stroke');
72219
72220                 enter
72221                     .append('path')
72222                     .call(markerPath, 'stroke');
72223
72224                 enter
72225                     .append('use')
72226                     .attr('transform', 'translate(-5, -19)')
72227                     .attr('class', 'icon')
72228                     .attr('width', '11px')
72229                     .attr('height', '11px');
72230
72231                 groups = groups
72232                     .merge(enter)
72233                     .attr('transform', svgPointTransform(projection))
72234                     .classed('added', function(d) {
72235                         return !base.entities[d.id]; // if it doesn't exist in the base graph, it's new
72236                     })
72237                     .classed('moved', function(d) {
72238                         return base.entities[d.id] && !fastDeepEqual(graph.entities[d.id].loc, base.entities[d.id].loc);
72239                     })
72240                     .classed('retagged', function(d) {
72241                         return base.entities[d.id] && !fastDeepEqual(graph.entities[d.id].tags, base.entities[d.id].tags);
72242                     })
72243                     .call(svgTagClasses());
72244
72245                 groups.select('.shadow');   // propagate bound data
72246                 groups.select('.stroke');   // propagate bound data
72247                 groups.select('.icon')      // propagate bound data
72248                     .attr('xlink:href', function(entity) {
72249                         var preset = _mainPresetIndex.match(entity, graph);
72250                         var picon = preset && preset.icon;
72251
72252                         if (!picon) {
72253                             return '';
72254                         } else {
72255                             var isMaki = /^maki-/.test(picon);
72256                             return '#' + picon + (isMaki ? '-11' : '');
72257                         }
72258                     });
72259
72260
72261                 // Draw touch targets..
72262                 touchLayer
72263                     .call(drawTargets, graph, points, filter);
72264             }
72265
72266
72267             return drawPoints;
72268         }
72269
72270         function svgTurns(projection, context) {
72271
72272             function icon(turn) {
72273                 var u = turn.u ? '-u' : '';
72274                 if (turn.no) { return '#iD-turn-no' + u; }
72275                 if (turn.only) { return '#iD-turn-only' + u; }
72276                 return '#iD-turn-yes' + u;
72277             }
72278
72279             function drawTurns(selection, graph, turns) {
72280
72281                 function turnTransform(d) {
72282                     var pxRadius = 50;
72283                     var toWay = graph.entity(d.to.way);
72284                     var toPoints = graph.childNodes(toWay)
72285                         .map(function (n) { return n.loc; })
72286                         .map(projection);
72287                     var toLength = geoPathLength(toPoints);
72288                     var mid = toLength / 2;    // midpoint of destination way
72289
72290                     var toNode = graph.entity(d.to.node);
72291                     var toVertex = graph.entity(d.to.vertex);
72292                     var a = geoAngle(toVertex, toNode, projection);
72293                     var o = projection(toVertex.loc);
72294                     var r = d.u ? 0                  // u-turn: no radius
72295                         : !toWay.__via ? pxRadius    // leaf way: put marker at pxRadius
72296                         : Math.min(mid, pxRadius);   // via way: prefer pxRadius, fallback to mid for very short ways
72297
72298                     return 'translate(' + (r * Math.cos(a) + o[0]) + ',' + (r * Math.sin(a) + o[1]) + ') ' +
72299                         'rotate(' + a * 180 / Math.PI + ')';
72300                 }
72301
72302
72303                 var drawLayer = selection.selectAll('.layer-osm.points .points-group.turns');
72304                 var touchLayer = selection.selectAll('.layer-touch.turns');
72305
72306                 // Draw turns..
72307                 var groups = drawLayer.selectAll('g.turn')
72308                     .data(turns, function(d) { return d.key; });
72309
72310                 // exit
72311                 groups.exit()
72312                     .remove();
72313
72314                 // enter
72315                 var groupsEnter = groups.enter()
72316                     .append('g')
72317                     .attr('class', function(d) { return 'turn ' + d.key; });
72318
72319                 var turnsEnter = groupsEnter
72320                     .filter(function(d) { return !d.u; });
72321
72322                 turnsEnter.append('rect')
72323                     .attr('transform', 'translate(-22, -12)')
72324                     .attr('width', '44')
72325                     .attr('height', '24');
72326
72327                 turnsEnter.append('use')
72328                     .attr('transform', 'translate(-22, -12)')
72329                     .attr('width', '44')
72330                     .attr('height', '24');
72331
72332                 var uEnter = groupsEnter
72333                     .filter(function(d) { return d.u; });
72334
72335                 uEnter.append('circle')
72336                     .attr('r', '16');
72337
72338                 uEnter.append('use')
72339                     .attr('transform', 'translate(-16, -16)')
72340                     .attr('width', '32')
72341                     .attr('height', '32');
72342
72343                 // update
72344                 groups = groups
72345                     .merge(groupsEnter)
72346                     .attr('opacity', function(d) { return d.direct === false ? '0.7' : null; })
72347                     .attr('transform', turnTransform);
72348
72349                 groups.select('use')
72350                     .attr('xlink:href', icon);
72351
72352                 groups.select('rect');      // propagate bound data
72353                 groups.select('circle');    // propagate bound data
72354
72355
72356                 // Draw touch targets..
72357                 var fillClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
72358                 groups = touchLayer.selectAll('g.turn')
72359                     .data(turns, function(d) { return d.key; });
72360
72361                 // exit
72362                 groups.exit()
72363                     .remove();
72364
72365                 // enter
72366                 groupsEnter = groups.enter()
72367                     .append('g')
72368                     .attr('class', function(d) { return 'turn ' + d.key; });
72369
72370                 turnsEnter = groupsEnter
72371                     .filter(function(d) { return !d.u; });
72372
72373                 turnsEnter.append('rect')
72374                     .attr('class', 'target ' + fillClass)
72375                     .attr('transform', 'translate(-22, -12)')
72376                     .attr('width', '44')
72377                     .attr('height', '24');
72378
72379                 uEnter = groupsEnter
72380                     .filter(function(d) { return d.u; });
72381
72382                 uEnter.append('circle')
72383                     .attr('class', 'target ' + fillClass)
72384                     .attr('r', '16');
72385
72386                 // update
72387                 groups = groups
72388                     .merge(groupsEnter)
72389                     .attr('transform', turnTransform);
72390
72391                 groups.select('rect');      // propagate bound data
72392                 groups.select('circle');    // propagate bound data
72393
72394
72395                 return this;
72396             }
72397
72398             return drawTurns;
72399         }
72400
72401         function svgVertices(projection, context) {
72402             var radiuses = {
72403                 //       z16-, z17,   z18+,  w/icon
72404                 shadow: [6,    7.5,   7.5,   12],
72405                 stroke: [2.5,  3.5,   3.5,   8],
72406                 fill:   [1,    1.5,   1.5,   1.5]
72407             };
72408
72409             var _currHoverTarget;
72410             var _currPersistent = {};
72411             var _currHover = {};
72412             var _prevHover = {};
72413             var _currSelected = {};
72414             var _prevSelected = {};
72415             var _radii = {};
72416
72417
72418             function sortY(a, b) {
72419                 return b.loc[1] - a.loc[1];
72420             }
72421
72422             // Avoid exit/enter if we're just moving stuff around.
72423             // The node will get a new version but we only need to run the update selection.
72424             function fastEntityKey(d) {
72425                 var mode = context.mode();
72426                 var isMoving = mode && /^(add|draw|drag|move|rotate)/.test(mode.id);
72427                 return isMoving ? d.id : osmEntity.key(d);
72428             }
72429
72430
72431             function draw(selection, graph, vertices, sets, filter) {
72432                 sets = sets || { selected: {}, important: {}, hovered: {} };
72433
72434                 var icons = {};
72435                 var directions = {};
72436                 var wireframe = context.surface().classed('fill-wireframe');
72437                 var zoom = geoScaleToZoom(projection.scale());
72438                 var z = (zoom < 17 ? 0 : zoom < 18 ? 1 : 2);
72439                 var activeID = context.activeID();
72440                 var base = context.history().base();
72441
72442
72443                 function getIcon(d) {
72444                     // always check latest entity, as fastEntityKey avoids enter/exit now
72445                     var entity = graph.entity(d.id);
72446                     if (entity.id in icons) { return icons[entity.id]; }
72447
72448                     icons[entity.id] =
72449                         entity.hasInterestingTags() &&
72450                         _mainPresetIndex.match(entity, graph).icon;
72451
72452                     return icons[entity.id];
72453                 }
72454
72455
72456                 // memoize directions results, return false for empty arrays (for use in filter)
72457                 function getDirections(entity) {
72458                     if (entity.id in directions) { return directions[entity.id]; }
72459
72460                     var angles = entity.directions(graph, projection);
72461                     directions[entity.id] = angles.length ? angles : false;
72462                     return angles;
72463                 }
72464
72465
72466                 function updateAttributes(selection) {
72467                     ['shadow', 'stroke', 'fill'].forEach(function(klass) {
72468                         var rads = radiuses[klass];
72469                         selection.selectAll('.' + klass)
72470                             .each(function(entity) {
72471                                 var i = z && getIcon(entity);
72472                                 var r = rads[i ? 3 : z];
72473
72474                                 // slightly increase the size of unconnected endpoints #3775
72475                                 if (entity.id !== activeID && entity.isEndpoint(graph) && !entity.isConnected(graph)) {
72476                                     r += 1.5;
72477                                 }
72478
72479                                 if (klass === 'shadow') {   // remember this value, so we don't need to
72480                                     _radii[entity.id] = r;  // recompute it when we draw the touch targets
72481                                 }
72482
72483                                 select(this)
72484                                     .attr('r', r)
72485                                     .attr('visibility', (i && klass === 'fill') ? 'hidden' : null);
72486                             });
72487                     });
72488                 }
72489
72490                 vertices.sort(sortY);
72491
72492                 var groups = selection.selectAll('g.vertex')
72493                     .filter(filter)
72494                     .data(vertices, fastEntityKey);
72495
72496                 // exit
72497                 groups.exit()
72498                     .remove();
72499
72500                 // enter
72501                 var enter = groups.enter()
72502                     .append('g')
72503                     .attr('class', function(d) { return 'node vertex ' + d.id; })
72504                     .order();
72505
72506                 enter
72507                     .append('circle')
72508                     .attr('class', 'shadow');
72509
72510                 enter
72511                     .append('circle')
72512                     .attr('class', 'stroke');
72513
72514                 // Vertices with tags get a fill.
72515                 enter.filter(function(d) { return d.hasInterestingTags(); })
72516                     .append('circle')
72517                     .attr('class', 'fill');
72518
72519                 // update
72520                 groups = groups
72521                     .merge(enter)
72522                     .attr('transform', svgPointTransform(projection))
72523                     .classed('sibling', function(d) { return d.id in sets.selected; })
72524                     .classed('shared', function(d) { return graph.isShared(d); })
72525                     .classed('endpoint', function(d) { return d.isEndpoint(graph); })
72526                     .classed('added', function(d) {
72527                         return !base.entities[d.id]; // if it doesn't exist in the base graph, it's new
72528                     })
72529                     .classed('moved', function(d) {
72530                         return base.entities[d.id] && !fastDeepEqual(graph.entities[d.id].loc, base.entities[d.id].loc);
72531                     })
72532                     .classed('retagged', function(d) {
72533                         return base.entities[d.id] && !fastDeepEqual(graph.entities[d.id].tags, base.entities[d.id].tags);
72534                     })
72535                     .call(updateAttributes);
72536
72537                 // Vertices with icons get a `use`.
72538                 var iconUse = groups
72539                     .selectAll('.icon')
72540                     .data(function data(d) { return zoom >= 17 && getIcon(d) ? [d] : []; }, fastEntityKey);
72541
72542                 // exit
72543                 iconUse.exit()
72544                     .remove();
72545
72546                 // enter
72547                 iconUse.enter()
72548                     .append('use')
72549                     .attr('class', 'icon')
72550                     .attr('width', '11px')
72551                     .attr('height', '11px')
72552                     .attr('transform', 'translate(-5.5, -5.5)')
72553                     .attr('xlink:href', function(d) {
72554                         var picon = getIcon(d);
72555                         var isMaki = /^maki-/.test(picon);
72556                         return '#' + picon + (isMaki ? '-11' : '');
72557                     });
72558
72559
72560                 // Vertices with directions get viewfields
72561                 var dgroups = groups
72562                     .selectAll('.viewfieldgroup')
72563                     .data(function data(d) { return zoom >= 18 && getDirections(d) ? [d] : []; }, fastEntityKey);
72564
72565                 // exit
72566                 dgroups.exit()
72567                     .remove();
72568
72569                 // enter/update
72570                 dgroups = dgroups.enter()
72571                     .insert('g', '.shadow')
72572                     .attr('class', 'viewfieldgroup')
72573                     .merge(dgroups);
72574
72575                 var viewfields = dgroups.selectAll('.viewfield')
72576                     .data(getDirections, function key(d) { return osmEntity.key(d); });
72577
72578                 // exit
72579                 viewfields.exit()
72580                     .remove();
72581
72582                 // enter/update
72583                 viewfields.enter()
72584                     .append('path')
72585                     .attr('class', 'viewfield')
72586                     .attr('d', 'M0,0H0')
72587                     .merge(viewfields)
72588                     .attr('marker-start', 'url(#ideditor-viewfield-marker' + (wireframe ? '-wireframe' : '') + ')')
72589                     .attr('transform', function(d) { return 'rotate(' + d + ')'; });
72590             }
72591
72592
72593             function drawTargets(selection, graph, entities, filter) {
72594                 var targetClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
72595                 var nopeClass = context.getDebug('target') ? 'red ' : 'nocolor ';
72596                 var getTransform = svgPointTransform(projection).geojson;
72597                 var activeID = context.activeID();
72598                 var data = { targets: [], nopes: [] };
72599
72600                 entities.forEach(function(node) {
72601                     if (activeID === node.id) { return; }   // draw no target on the activeID
72602
72603                     var vertexType = svgPassiveVertex(node, graph, activeID);
72604                     if (vertexType !== 0) {     // passive or adjacent - allow to connect
72605                         data.targets.push({
72606                             type: 'Feature',
72607                             id: node.id,
72608                             properties: {
72609                                 target: true,
72610                                 entity: node
72611                             },
72612                             geometry: node.asGeoJSON()
72613                         });
72614                     } else {
72615                         data.nopes.push({
72616                             type: 'Feature',
72617                             id: node.id + '-nope',
72618                             properties: {
72619                                 nope: true,
72620                                 target: true,
72621                                 entity: node
72622                             },
72623                             geometry: node.asGeoJSON()
72624                         });
72625                     }
72626                 });
72627
72628                 // Targets allow hover and vertex snapping
72629                 var targets = selection.selectAll('.vertex.target-allowed')
72630                     .filter(function(d) { return filter(d.properties.entity); })
72631                     .data(data.targets, function key(d) { return d.id; });
72632
72633                 // exit
72634                 targets.exit()
72635                     .remove();
72636
72637                 // enter/update
72638                 targets.enter()
72639                     .append('circle')
72640                     .attr('r', function(d) {
72641                         return _radii[d.id]
72642                           || radiuses.shadow[3];
72643                     })
72644                     .merge(targets)
72645                     .attr('class', function(d) {
72646                         return 'node vertex target target-allowed '
72647                         + targetClass + d.id;
72648                     })
72649                     .attr('transform', getTransform);
72650
72651
72652                 // NOPE
72653                 var nopes = selection.selectAll('.vertex.target-nope')
72654                     .filter(function(d) { return filter(d.properties.entity); })
72655                     .data(data.nopes, function key(d) { return d.id; });
72656
72657                 // exit
72658                 nopes.exit()
72659                     .remove();
72660
72661                 // enter/update
72662                 nopes.enter()
72663                     .append('circle')
72664                     .attr('r', function(d) { return (_radii[d.properties.entity.id] || radiuses.shadow[3]); })
72665                     .merge(nopes)
72666                     .attr('class', function(d) { return 'node vertex target target-nope ' + nopeClass + d.id; })
72667                     .attr('transform', getTransform);
72668             }
72669
72670
72671             // Points can also render as vertices:
72672             // 1. in wireframe mode or
72673             // 2. at higher zooms if they have a direction
72674             function renderAsVertex(entity, graph, wireframe, zoom) {
72675                 var geometry = entity.geometry(graph);
72676                 return geometry === 'vertex' || (geometry === 'point' && (
72677                     wireframe || (zoom >= 18 && entity.directions(graph, projection).length)
72678                 ));
72679             }
72680
72681
72682             function isEditedNode(node, base, head) {
72683                 var baseNode = base.entities[node.id];
72684                 var headNode = head.entities[node.id];
72685                 return !headNode ||
72686                     !baseNode ||
72687                     !fastDeepEqual(headNode.tags, baseNode.tags) ||
72688                     !fastDeepEqual(headNode.loc, baseNode.loc);
72689             }
72690
72691
72692             function getSiblingAndChildVertices(ids, graph, wireframe, zoom) {
72693                 var results = {};
72694
72695                 var seenIds = {};
72696
72697                 function addChildVertices(entity) {
72698
72699                     // avoid redundant work and infinite recursion of circular relations
72700                     if (seenIds[entity.id]) { return; }
72701                     seenIds[entity.id] = true;
72702
72703                     var geometry = entity.geometry(graph);
72704                     if (!context.features().isHiddenFeature(entity, graph, geometry)) {
72705                         var i;
72706                         if (entity.type === 'way') {
72707                             for (i = 0; i < entity.nodes.length; i++) {
72708                                 var child = graph.hasEntity(entity.nodes[i]);
72709                                 if (child) {
72710                                     addChildVertices(child);
72711                                 }
72712                             }
72713                         } else if (entity.type === 'relation') {
72714                             for (i = 0; i < entity.members.length; i++) {
72715                                 var member = graph.hasEntity(entity.members[i].id);
72716                                 if (member) {
72717                                     addChildVertices(member);
72718                                 }
72719                             }
72720                         } else if (renderAsVertex(entity, graph, wireframe, zoom)) {
72721                             results[entity.id] = entity;
72722                         }
72723                     }
72724                 }
72725
72726                 ids.forEach(function(id) {
72727                     var entity = graph.hasEntity(id);
72728                     if (!entity) { return; }
72729
72730                     if (entity.type === 'node') {
72731                         if (renderAsVertex(entity, graph, wireframe, zoom)) {
72732                             results[entity.id] = entity;
72733                             graph.parentWays(entity).forEach(function(entity) {
72734                                 addChildVertices(entity);
72735                             });
72736                         }
72737                     } else {  // way, relation
72738                         addChildVertices(entity);
72739                     }
72740                 });
72741
72742                 return results;
72743             }
72744
72745
72746             function drawVertices(selection, graph, entities, filter, extent, fullRedraw) {
72747                 var wireframe = context.surface().classed('fill-wireframe');
72748                 var visualDiff = context.surface().classed('highlight-edited');
72749                 var zoom = geoScaleToZoom(projection.scale());
72750                 var mode = context.mode();
72751                 var isMoving = mode && /^(add|draw|drag|move|rotate)/.test(mode.id);
72752                 var base = context.history().base();
72753
72754                 var drawLayer = selection.selectAll('.layer-osm.points .points-group.vertices');
72755                 var touchLayer = selection.selectAll('.layer-touch.points');
72756
72757                 if (fullRedraw) {
72758                     _currPersistent = {};
72759                     _radii = {};
72760                 }
72761
72762                 // Collect important vertices from the `entities` list..
72763                 // (during a partial redraw, it will not contain everything)
72764                 for (var i = 0; i < entities.length; i++) {
72765                     var entity = entities[i];
72766                     var geometry = entity.geometry(graph);
72767                     var keep = false;
72768
72769                     // a point that looks like a vertex..
72770                     if ((geometry === 'point') && renderAsVertex(entity, graph, wireframe, zoom)) {
72771                         _currPersistent[entity.id] = entity;
72772                         keep = true;
72773
72774                     // a vertex of some importance..
72775                     } else if (geometry === 'vertex' &&
72776                         (entity.hasInterestingTags() || entity.isEndpoint(graph) || entity.isConnected(graph)
72777                         || (visualDiff && isEditedNode(entity, base, graph)))) {
72778                         _currPersistent[entity.id] = entity;
72779                         keep = true;
72780                     }
72781
72782                     // whatever this is, it's not a persistent vertex..
72783                     if (!keep && !fullRedraw) {
72784                         delete _currPersistent[entity.id];
72785                     }
72786                 }
72787
72788                 // 3 sets of vertices to consider:
72789                 var sets = {
72790                     persistent: _currPersistent,  // persistent = important vertices (render always)
72791                     selected: _currSelected,      // selected + siblings of selected (render always)
72792                     hovered: _currHover           // hovered + siblings of hovered (render only in draw modes)
72793                 };
72794
72795                 var all = Object.assign({}, (isMoving ? _currHover : {}), _currSelected, _currPersistent);
72796
72797                 // Draw the vertices..
72798                 // The filter function controls the scope of what objects d3 will touch (exit/enter/update)
72799                 // Adjust the filter function to expand the scope beyond whatever entities were passed in.
72800                 var filterRendered = function(d) {
72801                     return d.id in _currPersistent || d.id in _currSelected || d.id in _currHover || filter(d);
72802                 };
72803                 drawLayer
72804                     .call(draw, graph, currentVisible(all), sets, filterRendered);
72805
72806                 // Draw touch targets..
72807                 // When drawing, render all targets (not just those affected by a partial redraw)
72808                 var filterTouch = function(d) {
72809                     return isMoving ? true : filterRendered(d);
72810                 };
72811                 touchLayer
72812                     .call(drawTargets, graph, currentVisible(all), filterTouch);
72813
72814
72815                 function currentVisible(which) {
72816                     return Object.keys(which)
72817                         .map(graph.hasEntity, graph)     // the current version of this entity
72818                         .filter(function (entity) { return entity && entity.intersects(extent, graph); });
72819                 }
72820             }
72821
72822
72823             // partial redraw - only update the selected items..
72824             drawVertices.drawSelected = function(selection, graph, extent) {
72825                 var wireframe = context.surface().classed('fill-wireframe');
72826                 var zoom = geoScaleToZoom(projection.scale());
72827
72828                 _prevSelected = _currSelected || {};
72829                 if (context.map().isInWideSelection()) {
72830                     _currSelected = {};
72831                     context.selectedIDs().forEach(function(id) {
72832                         var entity = graph.hasEntity(id);
72833                         if (!entity) { return; }
72834
72835                         if (entity.type === 'node') {
72836                             if (renderAsVertex(entity, graph, wireframe, zoom)) {
72837                                 _currSelected[entity.id] = entity;
72838                             }
72839                         }
72840                     });
72841
72842                 } else {
72843                     _currSelected = getSiblingAndChildVertices(context.selectedIDs(), graph, wireframe, zoom);
72844                 }
72845
72846                 // note that drawVertices will add `_currSelected` automatically if needed..
72847                 var filter = function(d) { return d.id in _prevSelected; };
72848                 drawVertices(selection, graph, Object.values(_prevSelected), filter, extent, false);
72849             };
72850
72851
72852             // partial redraw - only update the hovered items..
72853             drawVertices.drawHover = function(selection, graph, target, extent) {
72854                 if (target === _currHoverTarget) { return; }  // continue only if something changed
72855
72856                 var wireframe = context.surface().classed('fill-wireframe');
72857                 var zoom = geoScaleToZoom(projection.scale());
72858
72859                 _prevHover = _currHover || {};
72860                 _currHoverTarget = target;
72861                 var entity = target && target.properties && target.properties.entity;
72862
72863                 if (entity) {
72864                     _currHover = getSiblingAndChildVertices([entity.id], graph, wireframe, zoom);
72865                 } else {
72866                     _currHover = {};
72867                 }
72868
72869                 // note that drawVertices will add `_currHover` automatically if needed..
72870                 var filter = function(d) { return d.id in _prevHover; };
72871                 drawVertices(selection, graph, Object.values(_prevHover), filter, extent, false);
72872             };
72873
72874             return drawVertices;
72875         }
72876
72877         function utilBindOnce(target, type, listener, capture) {
72878             var typeOnce = type + '.once';
72879             function one() {
72880                 target.on(typeOnce, null);
72881                 listener.apply(this, arguments);
72882             }
72883             target.on(typeOnce, one, capture);
72884             return this;
72885         }
72886
72887         // Adapted from d3-zoom to handle pointer events.
72888
72889         // Ignore right-click, since that should open the context menu.
72890         function defaultFilter$2() {
72891           return !event.ctrlKey && !event.button;
72892         }
72893
72894         function defaultExtent$1() {
72895           var e = this;
72896           if (e instanceof SVGElement) {
72897             e = e.ownerSVGElement || e;
72898             if (e.hasAttribute('viewBox')) {
72899               e = e.viewBox.baseVal;
72900               return [[e.x, e.y], [e.x + e.width, e.y + e.height]];
72901             }
72902             return [[0, 0], [e.width.baseVal.value, e.height.baseVal.value]];
72903           }
72904           return [[0, 0], [e.clientWidth, e.clientHeight]];
72905         }
72906
72907         function defaultWheelDelta$1() {
72908           return -event.deltaY * (event.deltaMode === 1 ? 0.05 : event.deltaMode ? 1 : 0.002);
72909         }
72910
72911         function defaultConstrain$1(transform, extent, translateExtent) {
72912           var dx0 = transform.invertX(extent[0][0]) - translateExtent[0][0],
72913               dx1 = transform.invertX(extent[1][0]) - translateExtent[1][0],
72914               dy0 = transform.invertY(extent[0][1]) - translateExtent[0][1],
72915               dy1 = transform.invertY(extent[1][1]) - translateExtent[1][1];
72916           return transform.translate(
72917             dx1 > dx0 ? (dx0 + dx1) / 2 : Math.min(0, dx0) || Math.max(0, dx1),
72918             dy1 > dy0 ? (dy0 + dy1) / 2 : Math.min(0, dy0) || Math.max(0, dy1)
72919           );
72920         }
72921
72922         function utilZoomPan() {
72923           var filter = defaultFilter$2,
72924               extent = defaultExtent$1,
72925               constrain = defaultConstrain$1,
72926               wheelDelta = defaultWheelDelta$1,
72927               scaleExtent = [0, Infinity],
72928               translateExtent = [[-Infinity, -Infinity], [Infinity, Infinity]],
72929               interpolate = interpolateZoom,
72930               listeners = dispatch('start', 'zoom', 'end'),
72931               _wheelDelay = 150,
72932               _transform = identity$2,
72933               _activeGesture;
72934
72935           function zoom(selection) {
72936             selection
72937                 .on('pointerdown.zoom', pointerdown)
72938                 .on('wheel.zoom', wheeled)
72939                 .style('touch-action', 'none')
72940                 .style('-webkit-tap-highlight-color', 'rgba(0,0,0,0)');
72941
72942             select(window)
72943                 .on('pointermove.zoompan', pointermove)
72944                 .on('pointerup.zoompan pointercancel.zoompan', pointerup);
72945           }
72946
72947           zoom.transform = function(collection, transform, point) {
72948             var selection = collection.selection ? collection.selection() : collection;
72949             if (collection !== selection) {
72950               schedule(collection, transform, point);
72951             } else {
72952               selection.interrupt().each(function() {
72953                 gesture(this, arguments)
72954                     .start()
72955                     .zoom(null, typeof transform === 'function' ? transform.apply(this, arguments) : transform)
72956                     .end();
72957               });
72958             }
72959           };
72960
72961           zoom.scaleBy = function(selection, k, p) {
72962             zoom.scaleTo(selection, function() {
72963               var k0 = _transform.k,
72964                   k1 = typeof k === 'function' ? k.apply(this, arguments) : k;
72965               return k0 * k1;
72966             }, p);
72967           };
72968
72969           zoom.scaleTo = function(selection, k, p) {
72970             zoom.transform(selection, function() {
72971               var e = extent.apply(this, arguments),
72972                   t0 = _transform,
72973                   p0 = p == null ? centroid(e) : typeof p === 'function' ? p.apply(this, arguments) : p,
72974                   p1 = t0.invert(p0),
72975                   k1 = typeof k === 'function' ? k.apply(this, arguments) : k;
72976               return constrain(translate(scale(t0, k1), p0, p1), e, translateExtent);
72977             }, p);
72978           };
72979
72980           zoom.translateBy = function(selection, x, y) {
72981             zoom.transform(selection, function() {
72982               return constrain(_transform.translate(
72983                 typeof x === 'function' ? x.apply(this, arguments) : x,
72984                 typeof y === 'function' ? y.apply(this, arguments) : y
72985               ), extent.apply(this, arguments), translateExtent);
72986             });
72987           };
72988
72989           zoom.translateTo = function(selection, x, y, p) {
72990             zoom.transform(selection, function() {
72991               var e = extent.apply(this, arguments),
72992                   t = _transform,
72993                   p0 = p == null ? centroid(e) : typeof p === 'function' ? p.apply(this, arguments) : p;
72994               return constrain(identity$2.translate(p0[0], p0[1]).scale(t.k).translate(
72995                 typeof x === 'function' ? -x.apply(this, arguments) : -x,
72996                 typeof y === 'function' ? -y.apply(this, arguments) : -y
72997               ), e, translateExtent);
72998             }, p);
72999           };
73000
73001           function scale(transform, k) {
73002             k = Math.max(scaleExtent[0], Math.min(scaleExtent[1], k));
73003             return k === transform.k ? transform : new Transform(k, transform.x, transform.y);
73004           }
73005
73006           function translate(transform, p0, p1) {
73007             var x = p0[0] - p1[0] * transform.k, y = p0[1] - p1[1] * transform.k;
73008             return x === transform.x && y === transform.y ? transform : new Transform(transform.k, x, y);
73009           }
73010
73011           function centroid(extent) {
73012             return [(+extent[0][0] + +extent[1][0]) / 2, (+extent[0][1] + +extent[1][1]) / 2];
73013           }
73014
73015           function schedule(transition, transform, point) {
73016             transition
73017                 .on('start.zoom', function() { gesture(this, arguments).start(); })
73018                 .on('interrupt.zoom end.zoom', function() { gesture(this, arguments).end(); })
73019                 .tween('zoom', function() {
73020                   var that = this,
73021                       args = arguments,
73022                       g = gesture(that, args),
73023                       e = extent.apply(that, args),
73024                       p = point == null ? centroid(e) : typeof point === 'function' ? point.apply(that, args) : point,
73025                       w = Math.max(e[1][0] - e[0][0], e[1][1] - e[0][1]),
73026                       a = _transform,
73027                       b = typeof transform === 'function' ? transform.apply(that, args) : transform,
73028                       i = interpolate(a.invert(p).concat(w / a.k), b.invert(p).concat(w / b.k));
73029                   return function(t) {
73030                     if (t === 1) { t = b; } // Avoid rounding error on end.
73031                     else { var l = i(t), k = w / l[2]; t = new Transform(k, p[0] - l[0] * k, p[1] - l[1] * k); }
73032                     g.zoom(null, t);
73033                   };
73034                 });
73035           }
73036
73037           function gesture(that, args, clean) {
73038             return (!clean && _activeGesture) || new Gesture(that, args);
73039           }
73040
73041           function Gesture(that, args) {
73042             this.that = that;
73043             this.args = args;
73044             this.active = 0;
73045             this.extent = extent.apply(that, args);
73046           }
73047
73048           Gesture.prototype = {
73049             start: function() {
73050               if (++this.active === 1) {
73051                 _activeGesture = this;
73052                 this.emit('start');
73053               }
73054               return this;
73055             },
73056             zoom: function(key, transform) {
73057               if (this.mouse && key !== 'mouse') { this.mouse[1] = transform.invert(this.mouse[0]); }
73058               if (this.pointer0 && key !== 'touch') { this.pointer0[1] = transform.invert(this.pointer0[0]); }
73059               if (this.pointer1 && key !== 'touch') { this.pointer1[1] = transform.invert(this.pointer1[0]); }
73060               _transform = transform;
73061               this.emit('zoom');
73062               return this;
73063             },
73064             end: function() {
73065               if (--this.active === 0) {
73066                 _activeGesture = null;
73067                 this.emit('end');
73068               }
73069               return this;
73070             },
73071             emit: function(type) {
73072               customEvent(new ZoomEvent(zoom, type, _transform), listeners.apply, listeners, [type, this.that, this.args]);
73073             }
73074           };
73075
73076           function wheeled() {
73077             if (!filter.apply(this, arguments)) { return; }
73078             var g = gesture(this, arguments),
73079                 t = _transform,
73080                 k = Math.max(scaleExtent[0], Math.min(scaleExtent[1], t.k * Math.pow(2, wheelDelta.apply(this, arguments)))),
73081                 p = utilFastMouse(this)(event);
73082
73083             // If the mouse is in the same location as before, reuse it.
73084             // If there were recent wheel events, reset the wheel idle timeout.
73085             if (g.wheel) {
73086               if (g.mouse[0][0] !== p[0] || g.mouse[0][1] !== p[1]) {
73087                 g.mouse[1] = t.invert(g.mouse[0] = p);
73088               }
73089               clearTimeout(g.wheel);
73090
73091             // Otherwise, capture the mouse point and location at the start.
73092             } else {
73093               g.mouse = [p, t.invert(p)];
73094               interrupt(this);
73095               g.start();
73096             }
73097
73098             event.preventDefault();
73099             event.stopImmediatePropagation();
73100             g.wheel = setTimeout(wheelidled, _wheelDelay);
73101             g.zoom('mouse', constrain(translate(scale(t, k), g.mouse[0], g.mouse[1]), g.extent, translateExtent));
73102
73103             function wheelidled() {
73104               g.wheel = null;
73105               g.end();
73106             }
73107           }
73108
73109           var _downPointerIDs = new Set();
73110           var _pointerLocGetter;
73111
73112           function pointerdown() {
73113             _downPointerIDs.add(event.pointerId);
73114
73115             if (!filter.apply(this, arguments)) { return; }
73116
73117             var g = gesture(this, arguments, _downPointerIDs.size === 1);
73118             var started;
73119
73120             event.stopImmediatePropagation();
73121             _pointerLocGetter = utilFastMouse(this);
73122             var loc = _pointerLocGetter(event);
73123             var p = [loc, _transform.invert(loc), event.pointerId];
73124             if (!g.pointer0) {
73125                g.pointer0 = p;
73126                started = true;
73127
73128             } else if (!g.pointer1 && g.pointer0[2] !== p[2]) {
73129                g.pointer1 = p;
73130             }
73131
73132             if (started) {
73133               interrupt(this);
73134               g.start();
73135             }
73136           }
73137
73138           function pointermove() {
73139             if (!_downPointerIDs.has(event.pointerId)) { return; }
73140
73141             if (!_activeGesture || !_pointerLocGetter) { return; }
73142
73143             var g = gesture(this, arguments);
73144
73145             var isPointer0 = g.pointer0 && g.pointer0[2] === event.pointerId;
73146             var isPointer1 = !isPointer0 && g.pointer1 && g.pointer1[2] === event.pointerId;
73147
73148             if ((isPointer0 || isPointer1) && 'buttons' in event && !event.buttons) {
73149               // The pointer went up without ending the gesture somehow, e.g.
73150               // a down mouse was moved off the map and released. End it here.
73151               if (g.pointer0) { _downPointerIDs.delete(g.pointer0[2]); }
73152               if (g.pointer1) { _downPointerIDs.delete(g.pointer1[2]); }
73153               g.end();
73154               return;
73155             }
73156
73157             event.preventDefault();
73158             event.stopImmediatePropagation();
73159
73160             var loc = _pointerLocGetter(event);
73161             var t, p, l;
73162
73163             if (isPointer0) { g.pointer0[0] = loc; }
73164             else if (isPointer1) { g.pointer1[0] = loc; }
73165
73166             t = _transform;
73167             if (g.pointer1) {
73168               var p0 = g.pointer0[0], l0 = g.pointer0[1],
73169                   p1 = g.pointer1[0], l1 = g.pointer1[1],
73170                   dp = (dp = p1[0] - p0[0]) * dp + (dp = p1[1] - p0[1]) * dp,
73171                   dl = (dl = l1[0] - l0[0]) * dl + (dl = l1[1] - l0[1]) * dl;
73172               t = scale(t, Math.sqrt(dp / dl));
73173               p = [(p0[0] + p1[0]) / 2, (p0[1] + p1[1]) / 2];
73174               l = [(l0[0] + l1[0]) / 2, (l0[1] + l1[1]) / 2];
73175             } else if (g.pointer0) {
73176               p = g.pointer0[0];
73177               l = g.pointer0[1];
73178             }
73179             else { return; }
73180             g.zoom('touch', constrain(translate(t, p, l), g.extent, translateExtent));
73181           }
73182
73183           function pointerup() {
73184             if (!_downPointerIDs.has(event.pointerId)) { return; }
73185
73186             _downPointerIDs.delete(event.pointerId);
73187
73188             if (!_activeGesture) { return; }
73189
73190             var g = gesture(this, arguments);
73191
73192             event.stopImmediatePropagation();
73193
73194             if (g.pointer0 && g.pointer0[2] === event.pointerId) { delete g.pointer0; }
73195             else if (g.pointer1 && g.pointer1[2] === event.pointerId) { delete g.pointer1; }
73196
73197             if (g.pointer1 && !g.pointer0) {
73198               g.pointer0 = g.pointer1;
73199               delete g.pointer1;
73200             }
73201             if (g.pointer0) { g.pointer0[1] = _transform.invert(g.pointer0[0]); }
73202             else {
73203               g.end();
73204             }
73205           }
73206
73207           zoom.wheelDelta = function(_) {
73208             return arguments.length ? (wheelDelta = utilFunctor(+_), zoom) : wheelDelta;
73209           };
73210
73211           zoom.filter = function(_) {
73212             return arguments.length ? (filter = utilFunctor(!!_), zoom) : filter;
73213           };
73214
73215           zoom.extent = function(_) {
73216             return arguments.length ? (extent = utilFunctor([[+_[0][0], +_[0][1]], [+_[1][0], +_[1][1]]]), zoom) : extent;
73217           };
73218
73219           zoom.scaleExtent = function(_) {
73220             return arguments.length ? (scaleExtent[0] = +_[0], scaleExtent[1] = +_[1], zoom) : [scaleExtent[0], scaleExtent[1]];
73221           };
73222
73223           zoom.translateExtent = function(_) {
73224             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]]];
73225           };
73226
73227           zoom.constrain = function(_) {
73228             return arguments.length ? (constrain = _, zoom) : constrain;
73229           };
73230
73231           zoom.interpolate = function(_) {
73232             return arguments.length ? (interpolate = _, zoom) : interpolate;
73233           };
73234
73235           zoom._transform = function(_) {
73236             return arguments.length ? (_transform = _, zoom) : _transform;
73237           };
73238
73239           zoom.on = function() {
73240             var value = listeners.on.apply(listeners, arguments);
73241             return value === listeners ? zoom : value;
73242           };
73243
73244           return zoom;
73245         }
73246
73247         // A custom double-click / double-tap event detector that works on touch devices
73248         // if pointer events are supported. Falls back to default `dblclick` event.
73249         function utilDoubleUp() {
73250
73251             var dispatch$1 = dispatch('doubleUp');
73252
73253             var _maxTimespan = 500; // milliseconds
73254             var _maxDistance = 20; // web pixels; be somewhat generous to account for touch devices
73255             var _pointer; // object representing the pointer that could trigger double up
73256
73257             function pointerIsValidFor(loc) {
73258                 // second pointerup must occur within a small timeframe after the first pointerdown
73259                 return new Date().getTime() - _pointer.startTime <= _maxTimespan &&
73260                     // all pointer events must occur within a small distance of the first pointerdown
73261                     geoVecLength(_pointer.startLoc, loc) <= _maxDistance;
73262             }
73263
73264             function pointerdown() {
73265
73266                 // ignore right-click
73267                 if (event.ctrlKey || event.button === 2) { return; }
73268
73269                 var loc = [event.clientX, event.clientY];
73270
73271                 // Don't rely on pointerId here since it can change between pointerdown
73272                 // events on touch devices
73273                 if (_pointer && !pointerIsValidFor(loc)) {
73274                     // if this pointer is no longer valid, clear it so another can be started
73275                     _pointer = undefined;
73276                 }
73277
73278                 if (!_pointer) {
73279                     _pointer = {
73280                         startLoc: loc,
73281                         startTime: new Date().getTime(),
73282                         upCount: 0,
73283                         pointerId: event.pointerId
73284                     };
73285                 } else { // double down
73286                     _pointer.pointerId = event.pointerId;
73287                 }
73288             }
73289
73290             function pointerup() {
73291
73292                 // ignore right-click
73293                 if (event.ctrlKey || event.button === 2) { return; }
73294
73295                 if (!_pointer || _pointer.pointerId !== event.pointerId) { return; }
73296
73297                 _pointer.upCount += 1;
73298
73299                 if (_pointer.upCount === 2) { // double up!
73300                     var loc = [event.clientX, event.clientY];
73301                     if (pointerIsValidFor(loc)) {
73302                         var locInThis = utilFastMouse(this)(event);
73303                         dispatch$1.call('doubleUp', this, locInThis);
73304                     }
73305                     // clear the pointer info in any case
73306                     _pointer = undefined;
73307                 }
73308             }
73309
73310             function doubleUp(selection) {
73311                 if ('PointerEvent' in window) {
73312                     // dblclick isn't well supported on touch devices so manually use
73313                     // pointer events if they're available
73314                     selection
73315                         .on('pointerdown.doubleUp', pointerdown)
73316                         .on('pointerup.doubleUp', pointerup);
73317                 } else {
73318                     // fallback to dblclick
73319                     selection
73320                         .on('dblclick.doubleUp', function() {
73321                             dispatch$1.call('doubleUp', this, utilFastMouse(this)(event));
73322                         });
73323                 }
73324             }
73325
73326             doubleUp.off = function(selection) {
73327                 selection
73328                     .on('pointerdown.doubleUp', null)
73329                     .on('pointerup.doubleUp', null)
73330                     .on('dblclick.doubleUp', null);
73331             };
73332
73333             return utilRebind(doubleUp, dispatch$1, 'on');
73334         }
73335
73336         // constants
73337         var TILESIZE = 256;
73338         var minZoom = 2;
73339         var maxZoom = 24;
73340         var kMin = geoZoomToScale(minZoom, TILESIZE);
73341         var kMax = geoZoomToScale(maxZoom, TILESIZE);
73342
73343         function clamp(num, min, max) {
73344             return Math.max(min, Math.min(num, max));
73345         }
73346
73347
73348         function rendererMap(context) {
73349             var dispatch$1 = dispatch(
73350                 'move', 'drawn',
73351                 'crossEditableZoom', 'hitMinZoom',
73352                 'changeHighlighting', 'changeAreaFill'
73353             );
73354             var projection = context.projection;
73355             var curtainProjection = context.curtainProjection;
73356             var drawLayers;
73357             var drawPoints;
73358             var drawVertices;
73359             var drawLines;
73360             var drawAreas;
73361             var drawMidpoints;
73362             var drawLabels;
73363
73364             var _selection = select(null);
73365             var supersurface = select(null);
73366             var wrapper = select(null);
73367             var surface = select(null);
73368
73369             var _dimensions = [1, 1];
73370             var _dblClickZoomEnabled = true;
73371             var _redrawEnabled = true;
73372             var _gestureTransformStart;
73373             var _transformStart = projection.transform();
73374             var _transformLast;
73375             var _isTransformed = false;
73376             var _minzoom = 0;
73377             var _getMouseCoords;
73378             var _lastPointerEvent;
73379             var _lastWithinEditableZoom;
73380
73381             // whether a pointerdown event started the zoom
73382             var _pointerDown = false;
73383
73384             // use pointer events on supported platforms; fallback to mouse events
73385             var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
73386
73387             // use pointer event interaction if supported; fallback to touch/mouse events in d3-zoom
73388             var _zoomerPannerFunction = 'PointerEvent' in window ? utilZoomPan : d3_zoom;
73389
73390             var _zoomerPanner = _zoomerPannerFunction()
73391                 .scaleExtent([kMin, kMax])
73392                 .interpolate(interpolate)
73393                 .filter(zoomEventFilter)
73394                 .on('zoom.map', zoomPan)
73395                 .on('start.map', function() {
73396                     _pointerDown = event.sourceEvent && event.sourceEvent.type === 'pointerdown';
73397                 })
73398                 .on('end.map', function() {
73399                     _pointerDown = false;
73400                 });
73401             var _doubleUpHandler = utilDoubleUp();
73402
73403             var scheduleRedraw = throttle(redraw, 750);
73404             // var isRedrawScheduled = false;
73405             // var pendingRedrawCall;
73406             // function scheduleRedraw() {
73407             //     // Only schedule the redraw if one has not already been set.
73408             //     if (isRedrawScheduled) return;
73409             //     isRedrawScheduled = true;
73410             //     var that = this;
73411             //     var args = arguments;
73412             //     pendingRedrawCall = window.requestIdleCallback(function () {
73413             //         // Reset the boolean so future redraws can be set.
73414             //         isRedrawScheduled = false;
73415             //         redraw.apply(that, args);
73416             //     }, { timeout: 1400 });
73417             // }
73418
73419             function cancelPendingRedraw() {
73420                 scheduleRedraw.cancel();
73421                 // isRedrawScheduled = false;
73422                 // window.cancelIdleCallback(pendingRedrawCall);
73423             }
73424
73425
73426             function map(selection) {
73427                 _selection = selection;
73428
73429                 context
73430                     .on('change.map', immediateRedraw);
73431
73432                 var osm = context.connection();
73433                 if (osm) {
73434                     osm.on('change.map', immediateRedraw);
73435                 }
73436
73437                 function didUndoOrRedo(targetTransform) {
73438                     var mode = context.mode().id;
73439                     if (mode !== 'browse' && mode !== 'select') { return; }
73440                     if (targetTransform) {
73441                         map.transformEase(targetTransform);
73442                     }
73443                 }
73444
73445                 context.history()
73446                     .on('merge.map', function() { scheduleRedraw(); })
73447                     .on('change.map', immediateRedraw)
73448                     .on('undone.map', function(stack, fromStack) {
73449                         didUndoOrRedo(fromStack.transform);
73450                     })
73451                     .on('redone.map', function(stack) {
73452                         didUndoOrRedo(stack.transform);
73453                     });
73454
73455                 context.background()
73456                     .on('change.map', immediateRedraw);
73457
73458                 context.features()
73459                     .on('redraw.map', immediateRedraw);
73460
73461                 drawLayers
73462                     .on('change.map', function() {
73463                         context.background().updateImagery();
73464                         immediateRedraw();
73465                     });
73466
73467                 selection
73468                     .on('wheel.map mousewheel.map', function() {
73469                         // disable swipe-to-navigate browser pages on trackpad/magic mouse – #5552
73470                         event.preventDefault();
73471                     })
73472                     .call(_zoomerPanner)
73473                     .call(_zoomerPanner.transform, projection.transform())
73474                     .on('dblclick.zoom', null); // override d3-zoom dblclick handling
73475
73476                 map.supersurface = supersurface = selection.append('div')
73477                     .attr('class', 'supersurface')
73478                     .call(utilSetTransform, 0, 0);
73479
73480                 // Need a wrapper div because Opera can't cope with an absolutely positioned
73481                 // SVG element: http://bl.ocks.org/jfirebaugh/6fbfbd922552bf776c16
73482                 wrapper = supersurface
73483                     .append('div')
73484                     .attr('class', 'layer layer-data');
73485
73486                 map.surface = surface = wrapper
73487                     .call(drawLayers)
73488                     .selectAll('.surface');
73489
73490                 surface
73491                     .call(drawLabels.observe)
73492                     .call(_doubleUpHandler)
73493                     .on(_pointerPrefix + 'down.zoom', function() {
73494                         _lastPointerEvent = event;
73495                         if (event.button === 2) {
73496                             event.stopPropagation();
73497                         }
73498                     }, true)
73499                     .on(_pointerPrefix + 'up.zoom', function() {
73500                         _lastPointerEvent = event;
73501                         if (resetTransform()) {
73502                             immediateRedraw();
73503                         }
73504                     })
73505                     .on(_pointerPrefix + 'move.map', function() {
73506                         _lastPointerEvent = event;
73507                     })
73508                     .on(_pointerPrefix + 'over.vertices', function() {
73509                         if (map.editableDataEnabled() && !_isTransformed) {
73510                             var hover = event.target.__data__;
73511                             surface.call(drawVertices.drawHover, context.graph(), hover, map.extent());
73512                             dispatch$1.call('drawn', this, { full: false });
73513                         }
73514                     })
73515                     .on(_pointerPrefix + 'out.vertices', function() {
73516                         if (map.editableDataEnabled() && !_isTransformed) {
73517                             var hover = event.relatedTarget && event.relatedTarget.__data__;
73518                             surface.call(drawVertices.drawHover, context.graph(), hover, map.extent());
73519                             dispatch$1.call('drawn', this, { full: false });
73520                         }
73521                     });
73522
73523                 var detected = utilDetect();
73524
73525                 // only WebKit supports gesture events
73526                 if ('GestureEvent' in window &&
73527                     // Listening for gesture events on iOS 13.4+ breaks double-tapping,
73528                     // but we only need to do this on desktop Safari anyway. – #7694
73529                     !detected.isMobileWebKit) {
73530
73531                     // Desktop Safari sends gesture events for multitouch trackpad pinches.
73532                     // We can listen for these and translate them into map zooms.
73533                     surface
73534                         .on('gesturestart.surface', function() {
73535                             event.preventDefault();
73536                             _gestureTransformStart = projection.transform();
73537                         })
73538                         .on('gesturechange.surface', gestureChange);
73539                 }
73540
73541                 // must call after surface init
73542                 updateAreaFill();
73543
73544                 _doubleUpHandler.on('doubleUp.map', function(p0) {
73545                     if (!_dblClickZoomEnabled) { return; }
73546
73547                     // don't zoom if targeting something other than the map itself
73548                     if (typeof event.target.__data__ === 'object' &&
73549                         // or area fills
73550                         !select(event.target).classed('fill')) { return; }
73551
73552                     var zoomOut = event.shiftKey;
73553
73554                     var t = projection.transform();
73555
73556                     var p1 = t.invert(p0);
73557
73558                     t = t.scale(zoomOut ? 0.5 : 2);
73559
73560                     t.x = p0[0] - p1[0] * t.k;
73561                     t.y = p0[1] - p1[1] * t.k;
73562
73563                     map.transformEase(t);
73564                 });
73565
73566                 context.on('enter.map',  function() {
73567                     if (!map.editableDataEnabled(true /* skip zoom check */)) { return; }
73568
73569                     // redraw immediately any objects affected by a change in selectedIDs.
73570                     var graph = context.graph();
73571                     var selectedAndParents = {};
73572                     context.selectedIDs().forEach(function(id) {
73573                         var entity = graph.hasEntity(id);
73574                         if (entity) {
73575                             selectedAndParents[entity.id] = entity;
73576                             if (entity.type === 'node') {
73577                                 graph.parentWays(entity).forEach(function(parent) {
73578                                     selectedAndParents[parent.id] = parent;
73579                                 });
73580                             }
73581                         }
73582                     });
73583                     var data = Object.values(selectedAndParents);
73584                     var filter = function(d) { return d.id in selectedAndParents; };
73585
73586                     data = context.features().filter(data, graph);
73587
73588                     surface
73589                         .call(drawVertices.drawSelected, graph, map.extent())
73590                         .call(drawLines, graph, data, filter)
73591                         .call(drawAreas, graph, data, filter)
73592                         .call(drawMidpoints, graph, data, filter, map.trimmedExtent());
73593
73594                     dispatch$1.call('drawn', this, { full: false });
73595
73596                     // redraw everything else later
73597                     scheduleRedraw();
73598                 });
73599
73600                 map.dimensions(utilGetDimensions(selection));
73601             }
73602
73603
73604             function zoomEventFilter() {
73605                 // Fix for #2151, (see also d3/d3-zoom#60, d3/d3-brush#18)
73606                 // Intercept `mousedown` and check if there is an orphaned zoom gesture.
73607                 // This can happen if a previous `mousedown` occurred without a `mouseup`.
73608                 // If we detect this, dispatch `mouseup` to complete the orphaned gesture,
73609                 // so that d3-zoom won't stop propagation of new `mousedown` events.
73610                 if (event.type === 'mousedown') {
73611                     var hasOrphan = false;
73612                     var listeners = window.__on;
73613                     for (var i = 0; i < listeners.length; i++) {
73614                         var listener = listeners[i];
73615                         if (listener.name === 'zoom' && listener.type === 'mouseup') {
73616                             hasOrphan = true;
73617                             break;
73618                         }
73619                     }
73620                     if (hasOrphan) {
73621                         var event$1 = window.CustomEvent;
73622                         if (event$1) {
73623                             event$1 = new event$1('mouseup');
73624                         } else {
73625                             event$1 = window.document.createEvent('Event');
73626                             event$1.initEvent('mouseup', false, false);
73627                         }
73628                         // Event needs to be dispatched with an event.view property.
73629                         event$1.view = window;
73630                         window.dispatchEvent(event$1);
73631                     }
73632                 }
73633
73634                 return event.button !== 2;   // ignore right clicks
73635             }
73636
73637
73638             function pxCenter() {
73639                 return [_dimensions[0] / 2, _dimensions[1] / 2];
73640             }
73641
73642
73643             function drawEditable(difference, extent) {
73644                 var mode = context.mode();
73645                 var graph = context.graph();
73646                 var features = context.features();
73647                 var all = context.history().intersects(map.extent());
73648                 var fullRedraw = false;
73649                 var data;
73650                 var set;
73651                 var filter;
73652                 var applyFeatureLayerFilters = true;
73653
73654                 if (map.isInWideSelection()) {
73655                     data = [];
73656                     utilEntityAndDeepMemberIDs(mode.selectedIDs(), context.graph()).forEach(function(id) {
73657                         var entity = context.hasEntity(id);
73658                         if (entity) { data.push(entity); }
73659                     });
73660                     fullRedraw = true;
73661                     filter = utilFunctor(true);
73662                     // selected features should always be visible, so we can skip filtering
73663                     applyFeatureLayerFilters = false;
73664
73665                 } else if (difference) {
73666                     var complete = difference.complete(map.extent());
73667                     data = Object.values(complete).filter(Boolean);
73668                     set = new Set(Object.keys(complete));
73669                     filter = function(d) { return set.has(d.id); };
73670                     features.clear(data);
73671
73672                 } else {
73673                     // force a full redraw if gatherStats detects that a feature
73674                     // should be auto-hidden (e.g. points or buildings)..
73675                     if (features.gatherStats(all, graph, _dimensions)) {
73676                         extent = undefined;
73677                     }
73678
73679                     if (extent) {
73680                         data = context.history().intersects(map.extent().intersection(extent));
73681                         set = new Set(data.map(function(entity) { return entity.id; }));
73682                         filter = function(d) { return set.has(d.id); };
73683
73684                     } else {
73685                         data = all;
73686                         fullRedraw = true;
73687                         filter = utilFunctor(true);
73688                     }
73689                 }
73690
73691                 if (applyFeatureLayerFilters) {
73692                     data = features.filter(data, graph);
73693                 } else {
73694                     context.features().resetStats();
73695                 }
73696
73697                 if (mode && mode.id === 'select') {
73698                     // update selected vertices - the user might have just double-clicked a way,
73699                     // creating a new vertex, triggering a partial redraw without a mode change
73700                     surface.call(drawVertices.drawSelected, graph, map.extent());
73701                 }
73702
73703                 surface
73704                     .call(drawVertices, graph, data, filter, map.extent(), fullRedraw)
73705                     .call(drawLines, graph, data, filter)
73706                     .call(drawAreas, graph, data, filter)
73707                     .call(drawMidpoints, graph, data, filter, map.trimmedExtent())
73708                     .call(drawLabels, graph, data, filter, _dimensions, fullRedraw)
73709                     .call(drawPoints, graph, data, filter);
73710
73711                 dispatch$1.call('drawn', this, {full: true});
73712             }
73713
73714             map.init = function() {
73715                 drawLayers = svgLayers(projection, context);
73716                 drawPoints = svgPoints(projection, context);
73717                 drawVertices = svgVertices(projection, context);
73718                 drawLines = svgLines(projection, context);
73719                 drawAreas = svgAreas(projection, context);
73720                 drawMidpoints = svgMidpoints(projection, context);
73721                 drawLabels = svgLabels(projection, context);
73722             };
73723
73724             function editOff() {
73725                 context.features().resetStats();
73726                 surface.selectAll('.layer-osm *').remove();
73727                 surface.selectAll('.layer-touch:not(.markers) *').remove();
73728
73729                 var allowed = {
73730                     'browse': true,
73731                     'save': true,
73732                     'select-note': true,
73733                     'select-data': true,
73734                     'select-error': true
73735                 };
73736
73737                 var mode = context.mode();
73738                 if (mode && !allowed[mode.id]) {
73739                     context.enter(modeBrowse(context));
73740                 }
73741
73742                 dispatch$1.call('drawn', this, {full: true});
73743             }
73744
73745
73746
73747
73748
73749             function gestureChange() {
73750                 // Remap Safari gesture events to wheel events - #5492
73751                 // We want these disabled most places, but enabled for zoom/unzoom on map surface
73752                 // https://developer.mozilla.org/en-US/docs/Web/API/GestureEvent
73753                 var e = event;
73754                 e.preventDefault();
73755
73756                 var props = {
73757                     deltaMode: 0,    // dummy values to ignore in zoomPan
73758                     deltaY: 1,       // dummy values to ignore in zoomPan
73759                     clientX: e.clientX,
73760                     clientY: e.clientY,
73761                     screenX: e.screenX,
73762                     screenY: e.screenY,
73763                     x: e.x,
73764                     y: e.y
73765                 };
73766
73767                 var e2 = new WheelEvent('wheel', props);
73768                 e2._scale = e.scale;         // preserve the original scale
73769                 e2._rotation = e.rotation;   // preserve the original rotation
73770
73771                 _selection.node().dispatchEvent(e2);
73772             }
73773
73774
73775             function zoomPan(manualEvent) {
73776                 var event$1 = (manualEvent || event);
73777                 var source = event$1.sourceEvent;
73778                 var eventTransform = event$1.transform;
73779                 var x = eventTransform.x;
73780                 var y = eventTransform.y;
73781                 var k = eventTransform.k;
73782
73783                 // Special handling of 'wheel' events:
73784                 // They might be triggered by the user scrolling the mouse wheel,
73785                 // or 2-finger pinch/zoom gestures, the transform may need adjustment.
73786                 if (source && source.type === 'wheel') {
73787
73788                     // assume that the gesture is already handled by pointer events
73789                     if (_pointerDown) { return; }
73790
73791                     var detected = utilDetect();
73792                     var dX = source.deltaX;
73793                     var dY = source.deltaY;
73794                     var x2 = x;
73795                     var y2 = y;
73796                     var k2 = k;
73797                     var t0, p0, p1;
73798
73799                     // Normalize mousewheel scroll speed (Firefox) - #3029
73800                     // If wheel delta is provided in LINE units, recalculate it in PIXEL units
73801                     // We are essentially redoing the calculations that occur here:
73802                     //   https://github.com/d3/d3-zoom/blob/78563a8348aa4133b07cac92e2595c2227ca7cd7/src/zoom.js#L203
73803                     // See this for more info:
73804                     //   https://github.com/basilfx/normalize-wheel/blob/master/src/normalizeWheel.js
73805                     if (source.deltaMode === 1 /* LINE */) {
73806                         // Convert from lines to pixels, more if the user is scrolling fast.
73807                         // (I made up the exp function to roughly match Firefox to what Chrome does)
73808                         // These numbers should be floats, because integers are treated as pan gesture below.
73809                         var lines = Math.abs(source.deltaY);
73810                         var sign = (source.deltaY > 0) ? 1 : -1;
73811                         dY = sign * clamp(
73812                             Math.exp((lines - 1) * 0.75) * 4.000244140625,
73813                             4.000244140625,    // min
73814                             350.000244140625   // max
73815                         );
73816
73817                         // On Firefox Windows and Linux we always get +/- the scroll line amount (default 3)
73818                         // There doesn't seem to be any scroll acceleration.
73819                         // This multiplier increases the speed a little bit - #5512
73820                         if (detected.os !== 'mac') {
73821                             dY *= 5;
73822                         }
73823
73824                         // recalculate x2,y2,k2
73825                         t0 = _isTransformed ? _transformLast : _transformStart;
73826                         p0 = _getMouseCoords(source);
73827                         p1 = t0.invert(p0);
73828                         k2 = t0.k * Math.pow(2, -dY / 500);
73829                         k2 = clamp(k2, kMin, kMax);
73830                         x2 = p0[0] - p1[0] * k2;
73831                         y2 = p0[1] - p1[1] * k2;
73832
73833                     // 2 finger map pinch zooming (Safari) - #5492
73834                     // These are fake `wheel` events we made from Safari `gesturechange` events..
73835                     } else if (source._scale) {
73836                         // recalculate x2,y2,k2
73837                         t0 = _gestureTransformStart;
73838                         p0 = _getMouseCoords(source);
73839                         p1 = t0.invert(p0);
73840                         k2 = t0.k * source._scale;
73841                         k2 = clamp(k2, kMin, kMax);
73842                         x2 = p0[0] - p1[0] * k2;
73843                         y2 = p0[1] - p1[1] * k2;
73844
73845                     // 2 finger map pinch zooming (all browsers except Safari) - #5492
73846                     // Pinch zooming via the `wheel` event will always have:
73847                     // - `ctrlKey = true`
73848                     // - `deltaY` is not round integer pixels (ignore `deltaX`)
73849                     } else if (source.ctrlKey && !isInteger(dY)) {
73850                         dY *= 6;   // slightly scale up whatever the browser gave us
73851
73852                         // recalculate x2,y2,k2
73853                         t0 = _isTransformed ? _transformLast : _transformStart;
73854                         p0 = _getMouseCoords(source);
73855                         p1 = t0.invert(p0);
73856                         k2 = t0.k * Math.pow(2, -dY / 500);
73857                         k2 = clamp(k2, kMin, kMax);
73858                         x2 = p0[0] - p1[0] * k2;
73859                         y2 = p0[1] - p1[1] * k2;
73860
73861                     // Trackpad scroll zooming with shift or alt/option key down
73862                     } else if ((source.altKey || source.shiftKey) && isInteger(dY)) {
73863                         // recalculate x2,y2,k2
73864                         t0 = _isTransformed ? _transformLast : _transformStart;
73865                         p0 = _getMouseCoords(source);
73866                         p1 = t0.invert(p0);
73867                         k2 = t0.k * Math.pow(2, -dY / 500);
73868                         k2 = clamp(k2, kMin, kMax);
73869                         x2 = p0[0] - p1[0] * k2;
73870                         y2 = p0[1] - p1[1] * k2;
73871
73872                     // 2 finger map panning (Mac only, all browsers) - #5492, #5512
73873                     // Panning via the `wheel` event will always have:
73874                     // - `ctrlKey = false`
73875                     // - `deltaX`,`deltaY` are round integer pixels
73876                     } else if (detected.os === 'mac' && !source.ctrlKey && isInteger(dX) && isInteger(dY)) {
73877                         p1 = projection.translate();
73878                         x2 = p1[0] - dX;
73879                         y2 = p1[1] - dY;
73880                         k2 = projection.scale();
73881                         k2 = clamp(k2, kMin, kMax);
73882                     }
73883
73884                     // something changed - replace the event transform
73885                     if (x2 !== x || y2 !== y || k2 !== k) {
73886                         x = x2;
73887                         y = y2;
73888                         k = k2;
73889                         eventTransform = identity$2.translate(x2, y2).scale(k2);
73890                         if (_zoomerPanner._transform) {
73891                             // utilZoomPan interface
73892                             _zoomerPanner._transform(eventTransform);
73893                         } else {
73894                             // d3_zoom interface
73895                             _selection.node().__zoom = eventTransform;
73896                         }
73897                     }
73898
73899                 }
73900
73901                 if (_transformStart.x === x &&
73902                     _transformStart.y === y &&
73903                     _transformStart.k === k) {
73904                     return;  // no change
73905                 }
73906
73907                 var withinEditableZoom = map.withinEditableZoom();
73908                 if (_lastWithinEditableZoom !== withinEditableZoom) {
73909                     if (_lastWithinEditableZoom !== undefined) {
73910                         // notify that the map zoomed in or out over the editable zoom threshold
73911                         dispatch$1.call('crossEditableZoom', this, withinEditableZoom);
73912                     }
73913                     _lastWithinEditableZoom = withinEditableZoom;
73914                 }
73915
73916                 if (geoScaleToZoom(k, TILESIZE) < _minzoom) {
73917                     surface.interrupt();
73918                     dispatch$1.call('hitMinZoom', this, map);
73919                     setCenterZoom(map.center(), context.minEditableZoom(), 0, true);
73920                     scheduleRedraw();
73921                     dispatch$1.call('move', this, map);
73922                     return;
73923                 }
73924
73925                 projection.transform(eventTransform);
73926
73927                 var scale = k / _transformStart.k;
73928                 var tX = (x / scale - _transformStart.x) * scale;
73929                 var tY = (y / scale - _transformStart.y) * scale;
73930
73931                 if (context.inIntro()) {
73932                     curtainProjection.transform({
73933                         x: x - tX,
73934                         y: y - tY,
73935                         k: k
73936                     });
73937                 }
73938
73939                 if (source) {
73940                     _lastPointerEvent = event$1;
73941                 }
73942                 _isTransformed = true;
73943                 _transformLast = eventTransform;
73944                 utilSetTransform(supersurface, tX, tY, scale);
73945                 scheduleRedraw();
73946
73947                 dispatch$1.call('move', this, map);
73948
73949
73950                 function isInteger(val) {
73951                     return typeof val === 'number' && isFinite(val) && Math.floor(val) === val;
73952                 }
73953             }
73954
73955
73956             function resetTransform() {
73957                 if (!_isTransformed) { return false; }
73958
73959                 utilSetTransform(supersurface, 0, 0);
73960                 _isTransformed = false;
73961                 if (context.inIntro()) {
73962                     curtainProjection.transform(projection.transform());
73963                 }
73964                 return true;
73965             }
73966
73967
73968             function redraw(difference, extent) {
73969                 if (surface.empty() || !_redrawEnabled) { return; }
73970
73971                 // If we are in the middle of a zoom/pan, we can't do differenced redraws.
73972                 // It would result in artifacts where differenced entities are redrawn with
73973                 // one transform and unchanged entities with another.
73974                 if (resetTransform()) {
73975                     difference = extent = undefined;
73976                 }
73977
73978                 var zoom = map.zoom();
73979                 var z = String(~~zoom);
73980
73981                 if (surface.attr('data-zoom') !== z) {
73982                     surface.attr('data-zoom', z);
73983                 }
73984
73985                 // class surface as `lowzoom` around z17-z18.5 (based on latitude)
73986                 var lat = map.center()[1];
73987                 var lowzoom = linear$2()
73988                     .domain([-60, 0, 60])
73989                     .range([17, 18.5, 17])
73990                     .clamp(true);
73991
73992                 surface
73993                     .classed('low-zoom', zoom <= lowzoom(lat));
73994
73995
73996                 if (!difference) {
73997                     supersurface.call(context.background());
73998                     wrapper.call(drawLayers);
73999                 }
74000
74001                 // OSM
74002                 if (map.editableDataEnabled() || map.isInWideSelection()) {
74003                     context.loadTiles(projection);
74004                     drawEditable(difference, extent);
74005                 } else {
74006                     editOff();
74007                 }
74008
74009                 _transformStart = projection.transform();
74010
74011                 return map;
74012             }
74013
74014
74015
74016             var immediateRedraw = function(difference, extent) {
74017                 if (!difference && !extent) { cancelPendingRedraw(); }
74018                 redraw(difference, extent);
74019             };
74020
74021
74022             map.lastPointerEvent = function() {
74023                 return _lastPointerEvent;
74024             };
74025
74026
74027             map.mouse = function() {
74028                 var event$1 = _lastPointerEvent || event;
74029                 if (event$1) {
74030                     var s;
74031                     while ((s = event$1.sourceEvent)) { event$1 = s; }
74032                     return _getMouseCoords(event$1);
74033                 }
74034                 return null;
74035             };
74036
74037
74038             // returns Lng/Lat
74039             map.mouseCoordinates = function() {
74040                 var coord = map.mouse() || pxCenter();
74041                 return projection.invert(coord);
74042             };
74043
74044
74045             map.dblclickZoomEnable = function(val) {
74046                 if (!arguments.length) { return _dblClickZoomEnabled; }
74047                 _dblClickZoomEnabled = val;
74048                 return map;
74049             };
74050
74051
74052             map.redrawEnable = function(val) {
74053                 if (!arguments.length) { return _redrawEnabled; }
74054                 _redrawEnabled = val;
74055                 return map;
74056             };
74057
74058
74059             map.isTransformed = function() {
74060                 return _isTransformed;
74061             };
74062
74063
74064             function setTransform(t2, duration, force) {
74065                 var t = projection.transform();
74066                 if (!force && t2.k === t.k && t2.x === t.x && t2.y === t.y) { return false; }
74067
74068                 if (duration) {
74069                     _selection
74070                         .transition()
74071                         .duration(duration)
74072                         .on('start', function() { map.startEase(); })
74073                         .call(_zoomerPanner.transform, identity$2.translate(t2.x, t2.y).scale(t2.k));
74074                 } else {
74075                     projection.transform(t2);
74076                     _transformStart = t2;
74077                     _selection.call(_zoomerPanner.transform, _transformStart);
74078                 }
74079
74080                 return true;
74081             }
74082
74083
74084             function setCenterZoom(loc2, z2, duration, force) {
74085                 var c = map.center();
74086                 var z = map.zoom();
74087                 if (loc2[0] === c[0] && loc2[1] === c[1] && z2 === z && !force) { return false; }
74088
74089                 var proj = geoRawMercator().transform(projection.transform());  // copy projection
74090
74091                 var k2 = clamp(geoZoomToScale(z2, TILESIZE), kMin, kMax);
74092                 proj.scale(k2);
74093
74094                 var t = proj.translate();
74095                 var point = proj(loc2);
74096
74097                 var center = pxCenter();
74098                 t[0] += center[0] - point[0];
74099                 t[1] += center[1] - point[1];
74100
74101                 return setTransform(identity$2.translate(t[0], t[1]).scale(k2), duration, force);
74102             }
74103
74104
74105             map.pan = function(delta, duration) {
74106                 var t = projection.translate();
74107                 var k = projection.scale();
74108
74109                 t[0] += delta[0];
74110                 t[1] += delta[1];
74111
74112                 if (duration) {
74113                     _selection
74114                         .transition()
74115                         .duration(duration)
74116                         .on('start', function() { map.startEase(); })
74117                         .call(_zoomerPanner.transform, identity$2.translate(t[0], t[1]).scale(k));
74118                 } else {
74119                     projection.translate(t);
74120                     _transformStart = projection.transform();
74121                     _selection.call(_zoomerPanner.transform, _transformStart);
74122                     dispatch$1.call('move', this, map);
74123                     immediateRedraw();
74124                 }
74125
74126                 return map;
74127             };
74128
74129
74130             map.dimensions = function(val) {
74131                 if (!arguments.length) { return _dimensions; }
74132
74133                 _dimensions = val;
74134                 drawLayers.dimensions(_dimensions);
74135                 context.background().dimensions(_dimensions);
74136                 projection.clipExtent([[0, 0], _dimensions]);
74137                 _getMouseCoords = utilFastMouse(supersurface.node());
74138
74139                 scheduleRedraw();
74140                 return map;
74141             };
74142
74143
74144             function zoomIn(delta) {
74145                 setCenterZoom(map.center(), ~~map.zoom() + delta, 250, true);
74146             }
74147
74148             function zoomOut(delta) {
74149                 setCenterZoom(map.center(), ~~map.zoom() - delta, 250, true);
74150             }
74151
74152             map.zoomIn = function() { zoomIn(1); };
74153             map.zoomInFurther = function() { zoomIn(4); };
74154             map.canZoomIn = function() { return map.zoom() < maxZoom; };
74155
74156             map.zoomOut = function() { zoomOut(1); };
74157             map.zoomOutFurther = function() { zoomOut(4); };
74158             map.canZoomOut = function() { return map.zoom() > minZoom; };
74159
74160             map.center = function(loc2) {
74161                 if (!arguments.length) {
74162                     return projection.invert(pxCenter());
74163                 }
74164
74165                 if (setCenterZoom(loc2, map.zoom())) {
74166                     dispatch$1.call('move', this, map);
74167                 }
74168
74169                 scheduleRedraw();
74170                 return map;
74171             };
74172
74173             map.unobscuredCenterZoomEase = function(loc, zoom) {
74174                 var offset = map.unobscuredOffsetPx();
74175
74176                 var proj = geoRawMercator().transform(projection.transform());  // copy projection
74177                 // use the target zoom to calculate the offset center
74178                 proj.scale(geoZoomToScale(zoom, TILESIZE));
74179
74180                 var locPx = proj(loc);
74181                 var offsetLocPx = [locPx[0] + offset[0], locPx[1] + offset[1]];
74182                 var offsetLoc = proj.invert(offsetLocPx);
74183
74184                 map.centerZoomEase(offsetLoc, zoom);
74185             };
74186
74187             map.unobscuredOffsetPx = function() {
74188                 var openPane = context.container().select('.map-panes .map-pane.shown');
74189                 if (!openPane.empty()) {
74190                     return [openPane.node().offsetWidth/2, 0];
74191                 }
74192                 return [0, 0];
74193             };
74194
74195             map.zoom = function(z2) {
74196                 if (!arguments.length) {
74197                     return Math.max(geoScaleToZoom(projection.scale(), TILESIZE), 0);
74198                 }
74199
74200                 if (z2 < _minzoom) {
74201                     surface.interrupt();
74202                     dispatch$1.call('hitMinZoom', this, map);
74203                     z2 = context.minEditableZoom();
74204                 }
74205
74206                 if (setCenterZoom(map.center(), z2)) {
74207                     dispatch$1.call('move', this, map);
74208                 }
74209
74210                 scheduleRedraw();
74211                 return map;
74212             };
74213
74214
74215             map.centerZoom = function(loc2, z2) {
74216                 if (setCenterZoom(loc2, z2)) {
74217                     dispatch$1.call('move', this, map);
74218                 }
74219
74220                 scheduleRedraw();
74221                 return map;
74222             };
74223
74224
74225             map.zoomTo = function(entity) {
74226                 var extent = entity.extent(context.graph());
74227                 if (!isFinite(extent.area())) { return map; }
74228
74229                 var z2 = clamp(map.trimmedExtentZoom(extent), 0, 20);
74230                 return map.centerZoom(extent.center(), z2);
74231             };
74232
74233
74234             map.centerEase = function(loc2, duration) {
74235                 duration = duration || 250;
74236                 setCenterZoom(loc2, map.zoom(), duration);
74237                 return map;
74238             };
74239
74240
74241             map.zoomEase = function(z2, duration) {
74242                 duration = duration || 250;
74243                 setCenterZoom(map.center(), z2, duration, false);
74244                 return map;
74245             };
74246
74247
74248             map.centerZoomEase = function(loc2, z2, duration) {
74249                 duration = duration || 250;
74250                 setCenterZoom(loc2, z2, duration, false);
74251                 return map;
74252             };
74253
74254
74255             map.transformEase = function(t2, duration) {
74256                 duration = duration || 250;
74257                 setTransform(t2, duration, false /* don't force */);
74258                 return map;
74259             };
74260
74261
74262             map.zoomToEase = function(obj, duration) {
74263                 var extent;
74264                 if (Array.isArray(obj)) {
74265                     obj.forEach(function(entity) {
74266                         var entityExtent = entity.extent(context.graph());
74267                         if (!extent) {
74268                             extent = entityExtent;
74269                         } else {
74270                             extent = extent.extend(entityExtent);
74271                         }
74272                     });
74273                 } else {
74274                     extent = obj.extent(context.graph());
74275                 }
74276                 if (!isFinite(extent.area())) { return map; }
74277
74278                 var z2 = clamp(map.trimmedExtentZoom(extent), 0, 20);
74279                 return map.centerZoomEase(extent.center(), z2, duration);
74280             };
74281
74282
74283             map.startEase = function() {
74284                 utilBindOnce(surface, _pointerPrefix + 'down.ease', function() {
74285                     map.cancelEase();
74286                 });
74287                 return map;
74288             };
74289
74290
74291             map.cancelEase = function() {
74292                 _selection.interrupt();
74293                 return map;
74294             };
74295
74296
74297             map.extent = function(val) {
74298                 if (!arguments.length) {
74299                     return new geoExtent(
74300                         projection.invert([0, _dimensions[1]]),
74301                         projection.invert([_dimensions[0], 0])
74302                     );
74303                 } else {
74304                     var extent = geoExtent(val);
74305                     map.centerZoom(extent.center(), map.extentZoom(extent));
74306                 }
74307             };
74308
74309
74310             map.trimmedExtent = function(val) {
74311                 if (!arguments.length) {
74312                     var headerY = 71;
74313                     var footerY = 30;
74314                     var pad = 10;
74315                     return new geoExtent(
74316                         projection.invert([pad, _dimensions[1] - footerY - pad]),
74317                         projection.invert([_dimensions[0] - pad, headerY + pad])
74318                     );
74319                 } else {
74320                     var extent = geoExtent(val);
74321                     map.centerZoom(extent.center(), map.trimmedExtentZoom(extent));
74322                 }
74323             };
74324
74325
74326             function calcExtentZoom(extent, dim) {
74327                 var tl = projection([extent[0][0], extent[1][1]]);
74328                 var br = projection([extent[1][0], extent[0][1]]);
74329
74330                 // Calculate maximum zoom that fits extent
74331                 var hFactor = (br[0] - tl[0]) / dim[0];
74332                 var vFactor = (br[1] - tl[1]) / dim[1];
74333                 var hZoomDiff = Math.log(Math.abs(hFactor)) / Math.LN2;
74334                 var vZoomDiff = Math.log(Math.abs(vFactor)) / Math.LN2;
74335                 var newZoom = map.zoom() - Math.max(hZoomDiff, vZoomDiff);
74336
74337                 return newZoom;
74338             }
74339
74340
74341             map.extentZoom = function(val) {
74342                 return calcExtentZoom(geoExtent(val), _dimensions);
74343             };
74344
74345
74346             map.trimmedExtentZoom = function(val) {
74347                 var trimY = 120;
74348                 var trimX = 40;
74349                 var trimmed = [_dimensions[0] - trimX, _dimensions[1] - trimY];
74350                 return calcExtentZoom(geoExtent(val), trimmed);
74351             };
74352
74353
74354             map.withinEditableZoom = function() {
74355                 return map.zoom() >= context.minEditableZoom();
74356             };
74357
74358
74359             map.isInWideSelection = function() {
74360                 return !map.withinEditableZoom() && context.selectedIDs().length;
74361             };
74362
74363
74364             map.editableDataEnabled = function(skipZoomCheck) {
74365
74366                 var layer = context.layers().layer('osm');
74367                 if (!layer || !layer.enabled()) { return false; }
74368
74369                 return skipZoomCheck || map.withinEditableZoom();
74370             };
74371
74372
74373             map.notesEditable = function() {
74374                 var layer = context.layers().layer('notes');
74375                 if (!layer || !layer.enabled()) { return false; }
74376
74377                 return map.withinEditableZoom();
74378             };
74379
74380
74381             map.minzoom = function(val) {
74382                 if (!arguments.length) { return _minzoom; }
74383                 _minzoom = val;
74384                 return map;
74385             };
74386
74387
74388             map.toggleHighlightEdited = function() {
74389                 surface.classed('highlight-edited', !surface.classed('highlight-edited'));
74390                 map.pan([0,0]);  // trigger a redraw
74391                 dispatch$1.call('changeHighlighting', this);
74392             };
74393
74394
74395             map.areaFillOptions = ['wireframe', 'partial', 'full'];
74396
74397             map.activeAreaFill = function(val) {
74398                 if (!arguments.length) { return corePreferences('area-fill') || 'partial'; }
74399
74400                 corePreferences('area-fill', val);
74401                 if (val !== 'wireframe') {
74402                     corePreferences('area-fill-toggle', val);
74403                 }
74404                 updateAreaFill();
74405                 map.pan([0,0]);  // trigger a redraw
74406                 dispatch$1.call('changeAreaFill', this);
74407                 return map;
74408             };
74409
74410             map.toggleWireframe = function() {
74411
74412                 var activeFill = map.activeAreaFill();
74413
74414                 if (activeFill === 'wireframe') {
74415                     activeFill = corePreferences('area-fill-toggle') || 'partial';
74416                 } else {
74417                     activeFill = 'wireframe';
74418                 }
74419
74420                 map.activeAreaFill(activeFill);
74421             };
74422
74423             function updateAreaFill() {
74424                 var activeFill = map.activeAreaFill();
74425                 map.areaFillOptions.forEach(function(opt) {
74426                     surface.classed('fill-' + opt, Boolean(opt === activeFill));
74427                 });
74428             }
74429
74430
74431             map.layers = function () { return drawLayers; };
74432
74433
74434             map.doubleUpHandler = function() {
74435                 return _doubleUpHandler;
74436             };
74437
74438
74439             return utilRebind(map, dispatch$1, 'on');
74440         }
74441
74442         function rendererPhotos(context) {
74443             var dispatch$1 = dispatch('change');
74444             var _layerIDs = ['streetside', 'mapillary', 'mapillary-map-features', 'mapillary-signs', 'openstreetcam'];
74445             var _allPhotoTypes = ['flat', 'panoramic'];
74446             var _shownPhotoTypes = _allPhotoTypes.slice();   // shallow copy
74447
74448             function photos() {}
74449
74450             function updateStorage() {
74451                 if (window.mocha) { return; }
74452
74453                 var hash = utilStringQs(window.location.hash);
74454                 var enabled = context.layers().all().filter(function(d) {
74455                     return _layerIDs.indexOf(d.id) !== -1 && d.layer && d.layer.supported() && d.layer.enabled();
74456                 }).map(function(d) {
74457                     return d.id;
74458                 });
74459                 if (enabled.length) {
74460                     hash.photo_overlay = enabled.join(',');
74461                 } else {
74462                     delete hash.photo_overlay;
74463                 }
74464                 window.location.replace('#' + utilQsString(hash, true));
74465             }
74466
74467             photos.overlayLayerIDs = function() {
74468                 return _layerIDs;
74469             };
74470
74471             photos.allPhotoTypes = function() {
74472                 return _allPhotoTypes;
74473             };
74474
74475             function showsLayer(id) {
74476                 var layer = context.layers().layer(id);
74477                 return layer && layer.supported() && layer.enabled();
74478             }
74479
74480             photos.shouldFilterByPhotoType = function() {
74481                 return showsLayer('mapillary') ||
74482                     (showsLayer('streetside') && showsLayer('openstreetcam'));
74483             };
74484
74485             photos.showsPhotoType = function(val) {
74486                 if (!photos.shouldFilterByPhotoType()) { return true; }
74487
74488                 return _shownPhotoTypes.indexOf(val) !== -1;
74489             };
74490
74491             photos.showsFlat = function() {
74492                 return photos.showsPhotoType('flat');
74493             };
74494
74495             photos.showsPanoramic = function() {
74496                 return photos.showsPhotoType('panoramic');
74497             };
74498
74499             photos.togglePhotoType = function(val) {
74500                 var index = _shownPhotoTypes.indexOf(val);
74501                 if (index !== -1) {
74502                     _shownPhotoTypes.splice(index, 1);
74503                 } else {
74504                     _shownPhotoTypes.push(val);
74505                 }
74506                 dispatch$1.call('change', this);
74507                 return photos;
74508             };
74509
74510             photos.init = function() {
74511                 var hash = utilStringQs(window.location.hash);
74512                 if (hash.photo_overlay) {
74513                     var hashOverlayIDs = hash.photo_overlay.replace(/;/g, ',').split(',');
74514                     hashOverlayIDs.forEach(function(id) {
74515                         var layer = context.layers().layer(id);
74516                         if (layer) { layer.enabled(true); }
74517                     });
74518                 }
74519
74520                 context.layers().on('change.rendererPhotos', updateStorage);
74521             };
74522
74523             return utilRebind(photos, dispatch$1, 'on');
74524         }
74525
74526         function uiAccount(context) {
74527             var osm = context.connection();
74528
74529
74530             function update(selection) {
74531                 if (!osm) { return; }
74532
74533                 if (!osm.authenticated()) {
74534                     selection.selectAll('.userLink, .logoutLink')
74535                         .classed('hide', true);
74536                     return;
74537                 }
74538
74539                 osm.userDetails(function(err, details) {
74540                     var userLink = selection.select('.userLink'),
74541                         logoutLink = selection.select('.logoutLink');
74542
74543                     userLink.html('');
74544                     logoutLink.html('');
74545
74546                     if (err || !details) { return; }
74547
74548                     selection.selectAll('.userLink, .logoutLink')
74549                         .classed('hide', false);
74550
74551                     // Link
74552                     userLink.append('a')
74553                         .attr('href', osm.userURL(details.display_name))
74554                         .attr('target', '_blank');
74555
74556                     // Add thumbnail or dont
74557                     if (details.image_url) {
74558                         userLink.append('img')
74559                             .attr('class', 'icon pre-text user-icon')
74560                             .attr('src', details.image_url);
74561                     } else {
74562                         userLink
74563                             .call(svgIcon('#iD-icon-avatar', 'pre-text light'));
74564                     }
74565
74566                     // Add user name
74567                     userLink.append('span')
74568                         .attr('class', 'label')
74569                         .text(details.display_name);
74570
74571                     logoutLink.append('a')
74572                         .attr('class', 'logout')
74573                         .attr('href', '#')
74574                         .text(_t('logout'))
74575                         .on('click.logout', function() {
74576                             event.preventDefault();
74577                             osm.logout();
74578                         });
74579                 });
74580             }
74581
74582
74583             return function(selection) {
74584                 selection.append('li')
74585                     .attr('class', 'logoutLink')
74586                     .classed('hide', true);
74587
74588                 selection.append('li')
74589                     .attr('class', 'userLink')
74590                     .classed('hide', true);
74591
74592                 if (osm) {
74593                     osm.on('change.account', function() { update(selection); });
74594                     update(selection);
74595                 }
74596             };
74597         }
74598
74599         function uiAttribution(context) {
74600           var _selection = select(null);
74601
74602
74603           function render(selection, data, klass) {
74604             var div = selection.selectAll(("." + klass))
74605               .data([0]);
74606
74607             div = div.enter()
74608               .append('div')
74609               .attr('class', klass)
74610               .merge(div);
74611
74612
74613             var attributions = div.selectAll('.attribution')
74614               .data(data, function (d) { return d.id; });
74615
74616             attributions.exit()
74617               .remove();
74618
74619             attributions = attributions.enter()
74620               .append('span')
74621               .attr('class', 'attribution')
74622               .each(function (d, i, nodes) {
74623                 var attribution = select(nodes[i]);
74624
74625                 if (d.terms_html) {
74626                   attribution.html(d.terms_html);
74627                   return;
74628                 }
74629
74630                 if (d.terms_url) {
74631                   attribution = attribution
74632                     .append('a')
74633                     .attr('href', d.terms_url)
74634                     .attr('target', '_blank');
74635                 }
74636
74637                 var sourceID = d.id.replace(/\./g, '<TX_DOT>');
74638                 var terms_text = _t(("imagery." + sourceID + ".attribution.text"),
74639                   { default: d.terms_text || d.id || d.name() }
74640                 );
74641
74642                 if (d.icon && !d.overlay) {
74643                   attribution
74644                     .append('img')
74645                     .attr('class', 'source-image')
74646                     .attr('src', d.icon);
74647                 }
74648
74649                 attribution
74650                   .append('span')
74651                   .attr('class', 'attribution-text')
74652                   .text(terms_text);
74653               })
74654               .merge(attributions);
74655
74656
74657             var copyright = attributions.selectAll('.copyright-notice')
74658               .data(function (d) {
74659                 var notice = d.copyrightNotices(context.map().zoom(), context.map().extent());
74660                 return notice ? [notice] : [];
74661               });
74662
74663             copyright.exit()
74664               .remove();
74665
74666             copyright = copyright.enter()
74667               .append('span')
74668               .attr('class', 'copyright-notice')
74669               .merge(copyright);
74670
74671             copyright
74672               .text(String);
74673           }
74674
74675
74676           function update() {
74677             var baselayer = context.background().baseLayerSource();
74678             _selection
74679               .call(render, (baselayer ? [baselayer] : []), 'base-layer-attribution');
74680
74681             var z = context.map().zoom();
74682             var overlays = context.background().overlayLayerSources() || [];
74683             _selection
74684               .call(render, overlays.filter(function (s) { return s.validZoom(z); }), 'overlay-layer-attribution');
74685           }
74686
74687
74688           return function(selection) {
74689             _selection = selection;
74690
74691             context.background()
74692               .on('change.attribution', update);
74693
74694             context.map()
74695               .on('move.attribution', throttle(update, 400, { leading: false }));
74696
74697             update();
74698           };
74699         }
74700
74701         function uiContributors(context) {
74702             var osm = context.connection(),
74703                 debouncedUpdate = debounce(function() { update(); }, 1000),
74704                 limit = 4,
74705                 hidden = false,
74706                 wrap = select(null);
74707
74708
74709             function update() {
74710                 if (!osm) { return; }
74711
74712                 var users = {},
74713                     entities = context.history().intersects(context.map().extent());
74714
74715                 entities.forEach(function(entity) {
74716                     if (entity && entity.user) { users[entity.user] = true; }
74717                 });
74718
74719                 var u = Object.keys(users),
74720                     subset = u.slice(0, u.length > limit ? limit - 1 : limit);
74721
74722                 wrap.html('')
74723                     .call(svgIcon('#iD-icon-nearby', 'pre-text light'));
74724
74725                 var userList = select(document.createElement('span'));
74726
74727                 userList.selectAll()
74728                     .data(subset)
74729                     .enter()
74730                     .append('a')
74731                     .attr('class', 'user-link')
74732                     .attr('href', function(d) { return osm.userURL(d); })
74733                     .attr('target', '_blank')
74734                     .text(String);
74735
74736                 if (u.length > limit) {
74737                     var count = select(document.createElement('span'));
74738
74739                     count.append('a')
74740                         .attr('target', '_blank')
74741                         .attr('href', function() {
74742                             return osm.changesetsURL(context.map().center(), context.map().zoom());
74743                         })
74744                         .text(u.length - limit + 1);
74745
74746                     wrap.append('span')
74747                         .html(_t('contributors.truncated_list', { users: userList.html(), count: count.html() }));
74748
74749                 } else {
74750                     wrap.append('span')
74751                         .html(_t('contributors.list', { users: userList.html() }));
74752                 }
74753
74754                 if (!u.length) {
74755                     hidden = true;
74756                     wrap
74757                         .transition()
74758                         .style('opacity', 0);
74759
74760                 } else if (hidden) {
74761                     wrap
74762                         .transition()
74763                         .style('opacity', 1);
74764                 }
74765             }
74766
74767
74768             return function(selection) {
74769                 if (!osm) { return; }
74770                 wrap = selection;
74771                 update();
74772
74773                 osm.on('loaded.contributors', debouncedUpdate);
74774                 context.map().on('move.contributors', debouncedUpdate);
74775             };
74776         }
74777
74778         var _popoverID = 0;
74779
74780         function uiPopover(klass) {
74781             var _id = _popoverID++;
74782             var _anchorSelection = select(null);
74783             var popover = function(selection) {
74784                 _anchorSelection = selection;
74785                 selection.each(setup);
74786             };
74787             var _animation = utilFunctor(false);
74788             var _placement = utilFunctor('top'); // top, bottom, left, right
74789             var _alignment = utilFunctor('center');  // leading, center, trailing
74790             var _scrollContainer = utilFunctor(select(null));
74791             var _content;
74792             var _displayType = utilFunctor('');
74793             var _hasArrow = utilFunctor(true);
74794
74795             // use pointer events on supported platforms; fallback to mouse events
74796             var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
74797
74798             popover.displayType = function(val) {
74799                 if (arguments.length) {
74800                     _displayType = utilFunctor(val);
74801                     return popover;
74802                 } else {
74803                     return _displayType;
74804                 }
74805             };
74806
74807             popover.hasArrow = function(val) {
74808                 if (arguments.length) {
74809                     _hasArrow = utilFunctor(val);
74810                     return popover;
74811                 } else {
74812                     return _hasArrow;
74813                 }
74814             };
74815
74816             popover.placement = function(val) {
74817                 if (arguments.length) {
74818                     _placement = utilFunctor(val);
74819                     return popover;
74820                 } else {
74821                     return _placement;
74822                 }
74823             };
74824
74825             popover.alignment = function(val) {
74826                 if (arguments.length) {
74827                     _alignment = utilFunctor(val);
74828                     return popover;
74829                 } else {
74830                     return _alignment;
74831                 }
74832             };
74833
74834             popover.scrollContainer = function(val) {
74835                 if (arguments.length) {
74836                     _scrollContainer = utilFunctor(val);
74837                     return popover;
74838                 } else {
74839                     return _scrollContainer;
74840                 }
74841             };
74842
74843             popover.content = function(val) {
74844                 if (arguments.length) {
74845                     _content = val;
74846                     return popover;
74847                 } else {
74848                     return _content;
74849                 }
74850             };
74851
74852             popover.isShown = function() {
74853                 var popoverSelection = _anchorSelection.select('.popover-' + _id);
74854                 return !popoverSelection.empty() && popoverSelection.classed('in');
74855             };
74856
74857             popover.show = function() {
74858                 _anchorSelection.each(show);
74859             };
74860
74861             popover.updateContent = function() {
74862                 _anchorSelection.each(updateContent);
74863             };
74864
74865             popover.hide = function() {
74866                 _anchorSelection.each(hide);
74867             };
74868
74869             popover.toggle = function() {
74870                 _anchorSelection.each(toggle);
74871             };
74872
74873             popover.destroy = function(selection, selector) {
74874                 // by default, just destroy the current popover
74875                 selector = selector || '.popover-' + _id;
74876
74877                 selection
74878                     .on(_pointerPrefix + 'enter.popover', null)
74879                     .on(_pointerPrefix + 'leave.popover', null)
74880                     .on(_pointerPrefix + 'up.popover', null)
74881                     .on(_pointerPrefix + 'down.popover', null)
74882                     .on('click.popover', null)
74883                     .attr('title', function() {
74884                         return this.getAttribute('data-original-title') || this.getAttribute('title');
74885                     })
74886                     .attr('data-original-title', null)
74887                     .selectAll(selector)
74888                     .remove();
74889             };
74890
74891
74892             popover.destroyAny = function(selection) {
74893                 selection.call(popover.destroy, '.popover');
74894             };
74895
74896             function setup() {
74897                 var anchor = select(this);
74898                 var animate = _animation.apply(this, arguments);
74899                 var popoverSelection = anchor.selectAll('.popover-' + _id)
74900                     .data([0]);
74901
74902
74903                 var enter = popoverSelection.enter()
74904                     .append('div')
74905                     .attr('class', 'popover popover-' + _id + ' ' + (klass ? klass : ''))
74906                     .classed('arrowed', _hasArrow.apply(this, arguments));
74907
74908                 enter
74909                     .append('div')
74910                     .attr('class', 'popover-arrow');
74911
74912                 enter
74913                     .append('div')
74914                     .attr('class', 'popover-inner');
74915
74916                 popoverSelection = enter
74917                     .merge(popoverSelection);
74918
74919                 if (animate) {
74920                     popoverSelection.classed('fade', true);
74921                 }
74922
74923                 var display = _displayType.apply(this, arguments);
74924
74925                 if (display === 'hover') {
74926                     var _lastNonMouseEnterTime;
74927                     anchor.on(_pointerPrefix + 'enter.popover', function() {
74928
74929                         if (event.pointerType) {
74930                             if (event.pointerType !== 'mouse') {
74931                                 _lastNonMouseEnterTime = event.timeStamp;
74932                                 // only allow hover behavior for mouse input
74933                                 return;
74934                             } else if (_lastNonMouseEnterTime &&
74935                                 event.timeStamp - _lastNonMouseEnterTime < 1500) {
74936                                 // HACK: iOS 13.4 sends an erroneous `mouse` type pointerenter
74937                                 // event for non-mouse interactions right after sending
74938                                 // the correct type pointerenter event. Workaround by discarding
74939                                 // any mouse event that occurs immediately after a non-mouse event.
74940                                 return;
74941                             }
74942                         }
74943
74944                         // don't show if buttons are pressed, e.g. during click and drag of map
74945                         if (event.buttons !== 0) { return; }
74946
74947                         show.apply(this, arguments);
74948                     });
74949                     anchor.on(_pointerPrefix + 'leave.popover', function() {
74950                         hide.apply(this, arguments);
74951                     });
74952
74953                 } else if (display === 'clickFocus') {
74954                     anchor
74955                         .on(_pointerPrefix + 'down.popover', function() {
74956                             event.preventDefault();
74957                             event.stopPropagation();
74958                         })
74959                         .on(_pointerPrefix + 'up.popover', function() {
74960                             event.preventDefault();
74961                             event.stopPropagation();
74962                         })
74963                         .on('click.popover', toggle);
74964
74965                     popoverSelection
74966                         .attr('tabindex', 0)
74967                         .on('blur.popover', function() {
74968                             anchor.each(function() {
74969                                 hide.apply(this, arguments);
74970                             });
74971                         });
74972                 }
74973             }
74974
74975
74976             function show() {
74977                 var anchor = select(this);
74978                 var popoverSelection = anchor.selectAll('.popover-' + _id);
74979
74980                 if (popoverSelection.empty()) {
74981                     // popover was removed somehow, put it back
74982                     anchor.call(popover.destroy);
74983                     anchor.each(setup);
74984                     popoverSelection = anchor.selectAll('.popover-' + _id);
74985                 }
74986
74987                 popoverSelection.classed('in', true);
74988
74989                 var displayType = _displayType.apply(this, arguments);
74990                 if (displayType === 'clickFocus') {
74991                     anchor.classed('active', true);
74992                     popoverSelection.node().focus();
74993                 }
74994
74995                 anchor.each(updateContent);
74996             }
74997
74998             function updateContent() {
74999                 var anchor = select(this);
75000
75001                 if (_content) {
75002                     anchor.selectAll('.popover-' + _id + ' > .popover-inner')
75003                         .call(_content.apply(this, arguments));
75004                 }
75005
75006                 updatePosition.apply(this, arguments);
75007                 // hack: update multiple times to fix instances where the absolute offset is
75008                 // set before the dynamic popover size is calculated by the browser
75009                 updatePosition.apply(this, arguments);
75010                 updatePosition.apply(this, arguments);
75011             }
75012
75013
75014             function updatePosition() {
75015
75016                 var anchor = select(this);
75017                 var popoverSelection = anchor.selectAll('.popover-' + _id);
75018
75019                 var scrollContainer = _scrollContainer && _scrollContainer.apply(this, arguments);
75020                 var scrollNode = scrollContainer && !scrollContainer.empty() && scrollContainer.node();
75021                 var scrollLeft = scrollNode ? scrollNode.scrollLeft : 0;
75022                 var scrollTop = scrollNode ? scrollNode.scrollTop : 0;
75023
75024                 var placement = _placement.apply(this, arguments);
75025                 popoverSelection
75026                     .classed('left', false)
75027                     .classed('right', false)
75028                     .classed('top', false)
75029                     .classed('bottom', false)
75030                     .classed(placement, true);
75031
75032                 var alignment = _alignment.apply(this, arguments);
75033                 var alignFactor = 0.5;
75034                 if (alignment === 'leading') {
75035                     alignFactor = 0;
75036                 } else if (alignment === 'trailing') {
75037                     alignFactor = 1;
75038                 }
75039                 var anchorFrame = getFrame(anchor.node());
75040                 var popoverFrame = getFrame(popoverSelection.node());
75041                 var position;
75042
75043                 switch (placement) {
75044                     case 'top':
75045                     position = {
75046                         x: anchorFrame.x + (anchorFrame.w - popoverFrame.w) * alignFactor,
75047                         y: anchorFrame.y - popoverFrame.h
75048                     };
75049                     break;
75050                     case 'bottom':
75051                     position = {
75052                         x: anchorFrame.x + (anchorFrame.w - popoverFrame.w) * alignFactor,
75053                         y: anchorFrame.y + anchorFrame.h
75054                     };
75055                     break;
75056                     case 'left':
75057                     position = {
75058                         x: anchorFrame.x - popoverFrame.w,
75059                         y: anchorFrame.y + (anchorFrame.h - popoverFrame.h) * alignFactor
75060                     };
75061                     break;
75062                     case 'right':
75063                     position = {
75064                         x: anchorFrame.x + anchorFrame.w,
75065                         y: anchorFrame.y + (anchorFrame.h - popoverFrame.h) * alignFactor
75066                     };
75067                     break;
75068                 }
75069
75070                 if (position) {
75071
75072                     if (scrollNode && (placement === 'top' || placement === 'bottom')) {
75073
75074                         var initialPosX = position.x;
75075
75076                         if (position.x + popoverFrame.w > scrollNode.offsetWidth - 10) {
75077                             position.x = scrollNode.offsetWidth - 10 - popoverFrame.w;
75078                         } else if (position.x < 10) {
75079                             position.x = 10;
75080                         }
75081
75082                         var arrow = anchor.selectAll('.popover-' + _id + ' > .popover-arrow');
75083                         // keep the arrow centered on the button, or as close as possible
75084                         var arrowPosX = Math.min(Math.max(popoverFrame.w / 2 - (position.x - initialPosX), 10), popoverFrame.w - 10);
75085                         arrow.style('left', ~~arrowPosX + 'px');
75086                     }
75087
75088                     popoverSelection.style('left', ~~position.x + 'px').style('top', ~~position.y + 'px');
75089                 } else {
75090                     popoverSelection.style('left', null).style('top', null);
75091                 }
75092
75093                 function getFrame(node) {
75094                     var positionStyle = select(node).style('position');
75095                     if (positionStyle === 'absolute' || positionStyle === 'static') {
75096                         return {
75097                             x: node.offsetLeft - scrollLeft,
75098                             y: node.offsetTop - scrollTop,
75099                             w: node.offsetWidth,
75100                             h: node.offsetHeight
75101                         };
75102                     } else {
75103                         return {
75104                             x: 0,
75105                             y: 0,
75106                             w: node.offsetWidth,
75107                             h: node.offsetHeight
75108                         };
75109                     }
75110                 }
75111             }
75112
75113
75114             function hide() {
75115                 var anchor = select(this);
75116                 if (_displayType.apply(this, arguments) === 'clickFocus') {
75117                     anchor.classed('active', false);
75118                 }
75119                 anchor.selectAll('.popover-' + _id).classed('in', false);
75120             }
75121
75122
75123             function toggle() {
75124                 if (select(this).select('.popover-' + _id).classed('in')) {
75125                     hide.apply(this, arguments);
75126                 } else {
75127                     show.apply(this, arguments);
75128                 }
75129             }
75130
75131
75132             return popover;
75133         }
75134
75135         function uiTooltip(klass) {
75136
75137             var tooltip = uiPopover((klass || '') + ' tooltip')
75138                 .displayType('hover');
75139
75140             var _title = function() {
75141                 var title = this.getAttribute('data-original-title');
75142                 if (title) {
75143                     return title;
75144                 } else {
75145                     title = this.getAttribute('title');
75146                     this.removeAttribute('title');
75147                     this.setAttribute('data-original-title', title);
75148                 }
75149                 return title;
75150             };
75151
75152             var _heading = utilFunctor(null);
75153             var _keys = utilFunctor(null);
75154
75155             tooltip.title = function(val) {
75156                 if (!arguments.length) { return _title; }
75157                 _title = utilFunctor(val);
75158                 return tooltip;
75159             };
75160
75161             tooltip.heading = function(val) {
75162                 if (!arguments.length) { return _heading; }
75163                 _heading = utilFunctor(val);
75164                 return tooltip;
75165             };
75166
75167             tooltip.keys = function(val) {
75168                 if (!arguments.length) { return _keys; }
75169                 _keys = utilFunctor(val);
75170                 return tooltip;
75171             };
75172
75173             tooltip.content(function() {
75174                 var heading = _heading.apply(this, arguments);
75175                 var text = _title.apply(this, arguments);
75176                 var keys = _keys.apply(this, arguments);
75177
75178                 return function(selection) {
75179
75180                     var headingSelect = selection
75181                         .selectAll('.tooltip-heading')
75182                         .data(heading ? [heading] :[]);
75183
75184                     headingSelect.exit()
75185                         .remove();
75186
75187                     headingSelect.enter()
75188                         .append('div')
75189                         .attr('class', 'tooltip-heading')
75190                         .merge(headingSelect)
75191                         .html(heading);
75192
75193                     var textSelect = selection
75194                         .selectAll('.tooltip-text')
75195                         .data(text ? [text] :[]);
75196
75197                     textSelect.exit()
75198                         .remove();
75199
75200                     textSelect.enter()
75201                         .append('div')
75202                         .attr('class', 'tooltip-text')
75203                         .merge(textSelect)
75204                         .html(text);
75205
75206                     var keyhintWrap = selection
75207                         .selectAll('.keyhint-wrap')
75208                         .data(keys && keys.length ? [0] : []);
75209
75210                     keyhintWrap.exit()
75211                         .remove();
75212
75213                     var keyhintWrapEnter = keyhintWrap.enter()
75214                         .append('div')
75215                         .attr('class', 'keyhint-wrap');
75216
75217                     keyhintWrapEnter
75218                         .append('span')
75219                         .html(_t('tooltip_keyhint'));
75220
75221                     keyhintWrap = keyhintWrapEnter.merge(keyhintWrap);
75222
75223                     keyhintWrap.selectAll('kbd.shortcut')
75224                         .data(keys && keys.length ? keys : [])
75225                         .enter()
75226                         .append('kbd')
75227                         .attr('class', 'shortcut')
75228                         .html(function(d) {
75229                             return d;
75230                         });
75231                 };
75232             });
75233
75234             return tooltip;
75235         }
75236
75237         function uiEditMenu(context) {
75238             var dispatch$1 = dispatch('toggled');
75239
75240             var _menu = select(null);
75241             var _operations = [];
75242             // the position the menu should be displayed relative to
75243             var _anchorLoc = [0, 0];
75244             var _anchorLocLonLat = [0, 0];
75245             // a string indicating how the menu was opened
75246             var _triggerType = '';
75247
75248             var _vpTopMargin = 85; // viewport top margin
75249             var _vpBottomMargin = 45; // viewport bottom margin
75250             var _vpSideMargin = 35;   // viewport side margin
75251
75252             var _menuTop = false;
75253             var _menuHeight;
75254             var _menuWidth;
75255
75256             // hardcode these values to make menu positioning easier
75257             var _verticalPadding = 4;
75258
75259             // see also `.edit-menu .tooltip` CSS; include margin
75260             var _tooltipWidth = 210;
75261
75262             // offset the menu slightly from the target location
75263             var _menuSideMargin = 10;
75264
75265             var _tooltips = [];
75266
75267             var editMenu = function(selection) {
75268
75269                 var isTouchMenu = _triggerType.includes('touch') || _triggerType.includes('pen');
75270
75271                 var ops = _operations.filter(function(op) {
75272                     return !isTouchMenu || !op.mouseOnly;
75273                 });
75274
75275                 if (!ops.length) { return; }
75276
75277                 _tooltips = [];
75278
75279                 // Position the menu above the anchor for stylus and finger input
75280                 // since the mapper's hand likely obscures the screen below the anchor
75281                 _menuTop = isTouchMenu;
75282
75283                 // Show labels for touch input since there aren't hover tooltips
75284                 var showLabels = isTouchMenu;
75285
75286                 var buttonHeight = showLabels ? 32 : 34;
75287                 if (showLabels) {
75288                     // Get a general idea of the width based on the length of the label
75289                     _menuWidth = 52 + Math.min(120, 6 * Math.max.apply(Math, ops.map(function(op) {
75290                         return op.title.length;
75291                     })));
75292                 } else {
75293                     _menuWidth = 44;
75294                 }
75295
75296                 _menuHeight = _verticalPadding * 2 + ops.length * buttonHeight;
75297
75298                 _menu = selection
75299                     .append('div')
75300                     .attr('class', 'edit-menu')
75301                     .classed('touch-menu', isTouchMenu)
75302                     .style('padding', _verticalPadding + 'px 0');
75303
75304                 var buttons = _menu.selectAll('.edit-menu-item')
75305                     .data(ops);
75306
75307                 // enter
75308                 var buttonsEnter = buttons.enter()
75309                     .append('button')
75310                     .attr('class', function (d) { return 'edit-menu-item edit-menu-item-' + d.id; })
75311                     .style('height', buttonHeight + 'px')
75312                     .on('click', click)
75313                     // don't listen for `mouseup` because we only care about non-mouse pointer types
75314                     .on('pointerup', pointerup)
75315                     .on('pointerdown mousedown', function pointerdown() {
75316                         // don't let button presses also act as map input - #1869
75317                         event.stopPropagation();
75318                     });
75319
75320                 buttonsEnter.each(function(d) {
75321                     var tooltip = uiTooltip()
75322                         .heading(d.title)
75323                         .title(d.tooltip())
75324                         .keys([d.keys[0]]);
75325
75326                     _tooltips.push(tooltip);
75327
75328                     select(this)
75329                         .call(tooltip)
75330                         .append('div')
75331                         .attr('class', 'icon-wrap')
75332                         .call(svgIcon('#iD-operation-' + d.id, 'operation'));
75333                 });
75334
75335                 if (showLabels) {
75336                     buttonsEnter.append('span')
75337                         .attr('class', 'label')
75338                         .text(function(d) {
75339                             return d.title;
75340                         });
75341                 }
75342
75343                 // update
75344                 buttons = buttonsEnter
75345                     .merge(buttons)
75346                     .classed('disabled', function(d) { return d.disabled(); });
75347
75348                 updatePosition();
75349
75350                 var initialScale = context.projection.scale();
75351                 context.map()
75352                     .on('move.edit-menu', function() {
75353                         if (initialScale !== context.projection.scale()) {
75354                             editMenu.close();
75355                         }
75356                     })
75357                     .on('drawn.edit-menu', function(info) {
75358                         if (info.full) { updatePosition(); }
75359                     });
75360
75361                 var lastPointerUpType;
75362                 // `pointerup` is always called before `click`
75363                 function pointerup() {
75364                     lastPointerUpType = event.pointerType;
75365                 }
75366
75367                 function click(operation) {
75368                     event.stopPropagation();
75369                     if (operation.disabled()) {
75370                         if (lastPointerUpType === 'touch' ||
75371                             lastPointerUpType === 'pen') {
75372                             // there are no tooltips for touch interactions so flash feedback instead
75373                             context.ui().flash
75374                                 .duration(4000)
75375                                 .iconName('#iD-operation-' + operation.id)
75376                                 .iconClass('operation disabled')
75377                                 .text(operation.tooltip)();
75378                         }
75379                     } else {
75380                         if (lastPointerUpType === 'touch' ||
75381                             lastPointerUpType === 'pen') {
75382                             context.ui().flash
75383                                 .duration(2000)
75384                                 .iconName('#iD-operation-' + operation.id)
75385                                 .iconClass('operation')
75386                                 .text(operation.annotation() || operation.title)();
75387                         }
75388
75389                         operation();
75390                         editMenu.close();
75391                     }
75392                     lastPointerUpType = null;
75393                 }
75394
75395                 dispatch$1.call('toggled', this, true);
75396             };
75397
75398             function updatePosition() {
75399
75400                 if (!_menu || _menu.empty()) { return; }
75401
75402                 var anchorLoc = context.projection(_anchorLocLonLat);
75403
75404                 var viewport = context.surfaceRect();
75405
75406                 if (anchorLoc[0] < 0 ||
75407                     anchorLoc[0] > viewport.width ||
75408                     anchorLoc[1] < 0 ||
75409                     anchorLoc[1] > viewport.height) {
75410                     // close the menu if it's gone offscreen
75411
75412                     editMenu.close();
75413                     return;
75414                 }
75415
75416                 var menuLeft = displayOnLeft(viewport);
75417
75418                 var offset = [0, 0];
75419
75420                 offset[0] = menuLeft ? -1 * (_menuSideMargin + _menuWidth) : _menuSideMargin;
75421
75422                 if (_menuTop) {
75423                     if (anchorLoc[1] - _menuHeight < _vpTopMargin) {
75424                         // menu is near top viewport edge, shift downward
75425                         offset[1] = -anchorLoc[1] + _vpTopMargin;
75426                     } else {
75427                         offset[1] = -_menuHeight;
75428                     }
75429                 } else {
75430                     if (anchorLoc[1] + _menuHeight > (viewport.height - _vpBottomMargin)) {
75431                         // menu is near bottom viewport edge, shift upwards
75432                         offset[1] = -anchorLoc[1] - _menuHeight + viewport.height - _vpBottomMargin;
75433                     } else {
75434                         offset[1] = 0;
75435                     }
75436                 }
75437
75438                 var origin = geoVecAdd(anchorLoc, offset);
75439
75440                 _menu
75441                     .style('left', origin[0] + 'px')
75442                     .style('top', origin[1] + 'px');
75443
75444                 var tooltipSide = tooltipPosition(viewport, menuLeft);
75445                 _tooltips.forEach(function(tooltip) {
75446                     tooltip.placement(tooltipSide);
75447                 });
75448
75449                 function displayOnLeft(viewport) {
75450                     if (_mainLocalizer.textDirection() === 'ltr') {
75451                         if ((anchorLoc[0] + _menuSideMargin + _menuWidth) > (viewport.width - _vpSideMargin)) {
75452                             // right menu would be too close to the right viewport edge, go left
75453                             return true;
75454                         }
75455                         // prefer right menu
75456                         return false;
75457
75458                     } else { // rtl
75459                         if ((anchorLoc[0] - _menuSideMargin - _menuWidth) < _vpSideMargin) {
75460                             // left menu would be too close to the left viewport edge, go right
75461                             return false;
75462                         }
75463                         // prefer left menu
75464                         return true;
75465                     }
75466                 }
75467
75468                 function tooltipPosition(viewport, menuLeft) {
75469                     if (_mainLocalizer.textDirection() === 'ltr') {
75470                         if (menuLeft) {
75471                             // if there's not room for a right-side menu then there definitely
75472                             // isn't room for right-side tooltips
75473                             return 'left';
75474                         }
75475                         if ((anchorLoc[0] + _menuSideMargin + _menuWidth + _tooltipWidth) > (viewport.width - _vpSideMargin)) {
75476                             // right tooltips would be too close to the right viewport edge, go left
75477                             return 'left';
75478                         }
75479                         // prefer right tooltips
75480                         return 'right';
75481
75482                     } else { // rtl
75483                         if (!menuLeft) {
75484                             return 'right';
75485                         }
75486                         if ((anchorLoc[0] - _menuSideMargin - _menuWidth - _tooltipWidth) < _vpSideMargin) {
75487                             // left tooltips would be too close to the left viewport edge, go right
75488                             return 'right';
75489                         }
75490                         // prefer left tooltips
75491                         return 'left';
75492                     }
75493                 }
75494             }
75495
75496             editMenu.close = function () {
75497
75498                 context.map()
75499                     .on('move.edit-menu', null)
75500                     .on('drawn.edit-menu', null);
75501
75502                 _menu.remove();
75503                 _tooltips = [];
75504
75505                 dispatch$1.call('toggled', this, false);
75506             };
75507
75508             editMenu.anchorLoc = function(val) {
75509                 if (!arguments.length) { return _anchorLoc; }
75510                 _anchorLoc = val;
75511                 _anchorLocLonLat = context.projection.invert(_anchorLoc);
75512                 return editMenu;
75513             };
75514
75515             editMenu.triggerType = function(val) {
75516                 if (!arguments.length) { return _triggerType; }
75517                 _triggerType = val;
75518                 return editMenu;
75519             };
75520
75521             editMenu.operations = function(val) {
75522                 if (!arguments.length) { return _operations; }
75523                 _operations = val;
75524                 return editMenu;
75525             };
75526
75527             return utilRebind(editMenu, dispatch$1, 'on');
75528         }
75529
75530         function uiFeatureInfo(context) {
75531             function update(selection) {
75532                 var features = context.features();
75533                 var stats = features.stats();
75534                 var count = 0;
75535                 var hiddenList = features.hidden().map(function(k) {
75536                     if (stats[k]) {
75537                         count += stats[k];
75538                         return String(stats[k]) + ' ' + _t('feature.' + k + '.description');
75539                     }
75540                 }).filter(Boolean);
75541
75542                 selection.html('');
75543
75544                 if (hiddenList.length) {
75545                     var tooltipBehavior = uiTooltip()
75546                         .placement('top')
75547                         .title(function() {
75548                             return hiddenList.join('<br/>');
75549                         });
75550
75551                     selection.append('a')
75552                         .attr('class', 'chip')
75553                         .attr('href', '#')
75554                         .attr('tabindex', -1)
75555                         .html(_t('feature_info.hidden_warning', { count: count }))
75556                         .call(tooltipBehavior)
75557                         .on('click', function() {
75558                             tooltipBehavior.hide();
75559                             event.preventDefault();
75560                             // open the Map Data pane
75561                             context.ui().togglePanes(context.container().select('.map-panes .map-data-pane'));
75562                         });
75563                 }
75564
75565                 selection
75566                     .classed('hide', !hiddenList.length);
75567             }
75568
75569
75570             return function(selection) {
75571                 update(selection);
75572
75573                 context.features().on('change.feature_info', function() {
75574                     update(selection);
75575                 });
75576             };
75577         }
75578
75579         function uiFlash(context) {
75580             var _flashTimer;
75581
75582             var _duration = 2000;
75583             var _iconName = '#iD-icon-no';
75584             var _iconClass = 'disabled';
75585             var _text = '';
75586             var _textClass;
75587
75588             function flash() {
75589                 if (_flashTimer) {
75590                     _flashTimer.stop();
75591                 }
75592
75593                 context.container().select('.main-footer-wrap')
75594                     .classed('footer-hide', true)
75595                     .classed('footer-show', false);
75596                 context.container().select('.flash-wrap')
75597                     .classed('footer-hide', false)
75598                     .classed('footer-show', true);
75599
75600                 var content = context.container().select('.flash-wrap').selectAll('.flash-content')
75601                     .data([0]);
75602
75603                 // Enter
75604                 var contentEnter = content.enter()
75605                     .append('div')
75606                     .attr('class', 'flash-content');
75607
75608                 var iconEnter = contentEnter
75609                     .append('svg')
75610                     .attr('class', 'flash-icon icon')
75611                     .append('g')
75612                     .attr('transform', 'translate(10,10)');
75613
75614                 iconEnter
75615                     .append('circle')
75616                     .attr('r', 9);
75617
75618                 iconEnter
75619                     .append('use')
75620                     .attr('transform', 'translate(-7,-7)')
75621                     .attr('width', '14')
75622                     .attr('height', '14');
75623
75624                 contentEnter
75625                     .append('div')
75626                     .attr('class', 'flash-text');
75627
75628
75629                 // Update
75630                 content = content
75631                     .merge(contentEnter);
75632
75633                 content
75634                     .selectAll('.flash-icon')
75635                     .attr('class', 'icon flash-icon ' + (_iconClass || ''));
75636
75637                 content
75638                     .selectAll('.flash-icon use')
75639                     .attr('xlink:href', _iconName);
75640
75641                 content
75642                     .selectAll('.flash-text')
75643                     .attr('class', 'flash-text ' + (_textClass || ''))
75644                     .text(_text);
75645
75646
75647                 _flashTimer = d3_timeout(function() {
75648                     _flashTimer = null;
75649                     context.container().select('.main-footer-wrap')
75650                         .classed('footer-hide', false)
75651                         .classed('footer-show', true);
75652                     context.container().select('.flash-wrap')
75653                         .classed('footer-hide', true)
75654                         .classed('footer-show', false);
75655                 }, _duration);
75656
75657                 return content;
75658             }
75659
75660
75661             flash.duration = function(_) {
75662                 if (!arguments.length) { return _duration; }
75663                 _duration = _;
75664                 return flash;
75665             };
75666
75667             flash.text = function(_) {
75668                 if (!arguments.length) { return _text; }
75669                 _text = _;
75670                 return flash;
75671             };
75672
75673             flash.textClass = function(_) {
75674                 if (!arguments.length) { return _textClass; }
75675                 _textClass = _;
75676                 return flash;
75677             };
75678
75679             flash.iconName = function(_) {
75680                 if (!arguments.length) { return _iconName; }
75681                 _iconName = _;
75682                 return flash;
75683             };
75684
75685             flash.iconClass = function(_) {
75686                 if (!arguments.length) { return _iconClass; }
75687                 _iconClass = _;
75688                 return flash;
75689             };
75690
75691             return flash;
75692         }
75693
75694         function uiFullScreen(context) {
75695             var element = context.container().node();
75696             // var button = d3_select(null);
75697
75698
75699             function getFullScreenFn() {
75700                 if (element.requestFullscreen) {
75701                     return element.requestFullscreen;
75702                 } else if (element.msRequestFullscreen) {
75703                     return element.msRequestFullscreen;
75704                 } else if (element.mozRequestFullScreen) {
75705                     return element.mozRequestFullScreen;
75706                 } else if (element.webkitRequestFullscreen) {
75707                     return element.webkitRequestFullscreen;
75708                 }
75709             }
75710
75711
75712             function getExitFullScreenFn() {
75713                 if (document.exitFullscreen) {
75714                     return document.exitFullscreen;
75715                 } else if (document.msExitFullscreen) {
75716                     return document.msExitFullscreen;
75717                 } else if (document.mozCancelFullScreen) {
75718                     return document.mozCancelFullScreen;
75719                 } else if (document.webkitExitFullscreen) {
75720                     return document.webkitExitFullscreen;
75721                 }
75722             }
75723
75724
75725             function isFullScreen() {
75726                 return document.fullscreenElement ||
75727                     document.mozFullScreenElement ||
75728                     document.webkitFullscreenElement ||
75729                     document.msFullscreenElement;
75730             }
75731
75732
75733             function isSupported() {
75734                 return !!getFullScreenFn();
75735             }
75736
75737
75738             function fullScreen() {
75739                 event.preventDefault();
75740                 if (!isFullScreen()) {
75741                     // button.classed('active', true);
75742                     getFullScreenFn().apply(element);
75743                 } else {
75744                     // button.classed('active', false);
75745                     getExitFullScreenFn().apply(document);
75746                 }
75747             }
75748
75749
75750             return function() { // selection) {
75751                 if (!isSupported()) { return; }
75752
75753                 // button = selection.append('button')
75754                 //     .attr('title', t('full_screen'))
75755                 //     .attr('tabindex', -1)
75756                 //     .on('click', fullScreen)
75757                 //     .call(tooltip);
75758
75759                 // button.append('span')
75760                 //     .attr('class', 'icon full-screen');
75761
75762                 var detected = utilDetect();
75763                 var keys = (detected.os === 'mac' ? [uiCmd('⌃⌘F'), 'f11'] : ['f11']);
75764                 context.keybinding().on(keys, fullScreen);
75765             };
75766         }
75767
75768         function uiGeolocate(context) {
75769             var _geolocationOptions = {
75770                 // prioritize speed and power usage over precision
75771                 enableHighAccuracy: false,
75772                 // don't hang indefinitely getting the location
75773                 timeout: 6000 // 6sec
75774             };
75775             var _locating = uiLoading(context).message(_t('geolocate.locating')).blocking(true);
75776             var _layer = context.layers().layer('geolocate');
75777             var _position;
75778             var _extent;
75779             var _timeoutID;
75780             var _button = select(null);
75781
75782             function click() {
75783                 if (context.inIntro()) { return; }
75784                 if (!_layer.enabled() && !_locating.isShown()) {
75785
75786                     // This timeout ensures that we still call finish() even if
75787                     // the user declines to share their location in Firefox
75788                     _timeoutID = setTimeout(error, 10000 /* 10sec */ );
75789
75790                     context.container().call(_locating);
75791                     // get the latest position even if we already have one
75792                     navigator.geolocation.getCurrentPosition(success, error, _geolocationOptions);
75793                 } else {
75794                     _locating.close();
75795                     _layer.enabled(null, false);
75796                     updateButtonState();
75797                 }
75798             }
75799
75800             function zoomTo() {
75801                 context.enter(modeBrowse(context));
75802
75803                 var map = context.map();
75804                 _layer.enabled(_position, true);
75805                 updateButtonState();
75806                 map.centerZoomEase(_extent.center(), Math.min(20, map.extentZoom(_extent)));
75807             }
75808
75809             function success(geolocation) {
75810                 _position = geolocation;
75811                 var coords = _position.coords;
75812                 _extent = geoExtent([coords.longitude, coords.latitude]).padByMeters(coords.accuracy);
75813                 zoomTo();
75814                 finish();
75815             }
75816
75817             function error() {
75818                 if (_position) {
75819                     // use the position from a previous call if we have one
75820                     zoomTo();
75821                 } else {
75822                     context.ui().flash
75823                         .text(_t('geolocate.location_unavailable'))
75824                         .iconName('#iD-icon-geolocate')();
75825                 }
75826
75827                 finish();
75828             }
75829
75830             function finish() {
75831                 _locating.close();  // unblock ui
75832                 if (_timeoutID) { clearTimeout(_timeoutID); }
75833                 _timeoutID = undefined;
75834             }
75835
75836             function updateButtonState() {
75837                 _button.classed('active', _layer.enabled());
75838             }
75839
75840             return function(selection) {
75841                 if (!navigator.geolocation || !navigator.geolocation.getCurrentPosition) { return; }
75842
75843                 _button = selection
75844                     .append('button')
75845                     .on('click', click)
75846                     .call(svgIcon('#iD-icon-geolocate', 'light'))
75847                     .call(uiTooltip()
75848                         .placement((_mainLocalizer.textDirection() === 'rtl') ? 'right' : 'left')
75849                         .title(_t('geolocate.title'))
75850                         .keys([_t('geolocate.key')])
75851                     );
75852
75853                 context.keybinding().on(_t('geolocate.key'), click);
75854             };
75855         }
75856
75857         function uiPanelBackground(context) {
75858             var background = context.background();
75859             var currSourceName = null;
75860             var metadata = {};
75861             var metadataKeys = [
75862                 'zoom', 'vintage', 'source', 'description', 'resolution', 'accuracy'
75863             ];
75864
75865             var debouncedRedraw = debounce(redraw, 250);
75866
75867             function redraw(selection) {
75868                 var source = background.baseLayerSource();
75869                 if (!source) { return; }
75870
75871                 var isDG = (source.id.match(/^DigitalGlobe/i) !== null);
75872
75873                 if (currSourceName !== source.name()) {
75874                     currSourceName = source.name();
75875                     metadata = {};
75876                 }
75877
75878                 selection.html('');
75879
75880                 var list = selection
75881                     .append('ul')
75882                     .attr('class', 'background-info');
75883
75884                 list
75885                     .append('li')
75886                     .text(currSourceName);
75887
75888                 metadataKeys.forEach(function(k) {
75889                     // DigitalGlobe vintage is available in raster layers for now.
75890                     if (isDG && k === 'vintage') { return; }
75891
75892                     list
75893                         .append('li')
75894                         .attr('class', 'background-info-list-' + k)
75895                         .classed('hide', !metadata[k])
75896                         .text(_t('info_panels.background.' + k) + ':')
75897                         .append('span')
75898                         .attr('class', 'background-info-span-' + k)
75899                         .text(metadata[k]);
75900                 });
75901
75902                 debouncedGetMetadata(selection);
75903
75904                 var toggleTiles = context.getDebug('tile') ? 'hide_tiles' : 'show_tiles';
75905
75906                 selection
75907                     .append('a')
75908                     .text(_t('info_panels.background.' + toggleTiles))
75909                     .attr('href', '#')
75910                     .attr('class', 'button button-toggle-tiles')
75911                     .on('click', function() {
75912                         event.preventDefault();
75913                         context.setDebug('tile', !context.getDebug('tile'));
75914                         selection.call(redraw);
75915                     });
75916
75917                 if (isDG) {
75918                     var key = source.id + '-vintage';
75919                     var sourceVintage = context.background().findSource(key);
75920                     var showsVintage = context.background().showsLayer(sourceVintage);
75921                     var toggleVintage = showsVintage ? 'hide_vintage' : 'show_vintage';
75922                     selection
75923                         .append('a')
75924                         .text(_t('info_panels.background.' + toggleVintage))
75925                         .attr('href', '#')
75926                         .attr('class', 'button button-toggle-vintage')
75927                         .on('click', function() {
75928                             event.preventDefault();
75929                             context.background().toggleOverlayLayer(sourceVintage);
75930                             selection.call(redraw);
75931                         });
75932                 }
75933
75934                 // disable if necessary
75935                 ['DigitalGlobe-Premium', 'DigitalGlobe-Standard'].forEach(function(layerId) {
75936                     if (source.id !== layerId) {
75937                         var key = layerId + '-vintage';
75938                         var sourceVintage = context.background().findSource(key);
75939                         if (context.background().showsLayer(sourceVintage)) {
75940                             context.background().toggleOverlayLayer(sourceVintage);
75941                         }
75942                     }
75943                 });
75944             }
75945
75946
75947             var debouncedGetMetadata = debounce(getMetadata, 250);
75948
75949             function getMetadata(selection) {
75950                 var tile = context.container().select('.layer-background img.tile-center');   // tile near viewport center
75951                 if (tile.empty()) { return; }
75952
75953                 var sourceName = currSourceName;
75954                 var d = tile.datum();
75955                 var zoom = (d && d.length >= 3 && d[2]) || Math.floor(context.map().zoom());
75956                 var center = context.map().center();
75957
75958                 // update zoom
75959                 metadata.zoom = String(zoom);
75960                 selection.selectAll('.background-info-list-zoom')
75961                     .classed('hide', false)
75962                     .selectAll('.background-info-span-zoom')
75963                     .text(metadata.zoom);
75964
75965                 if (!d || !d.length >= 3) { return; }
75966
75967                 background.baseLayerSource().getMetadata(center, d, function(err, result) {
75968                     if (err || currSourceName !== sourceName) { return; }
75969
75970                     // update vintage
75971                     var vintage = result.vintage;
75972                     metadata.vintage = (vintage && vintage.range) || _t('info_panels.background.unknown');
75973                     selection.selectAll('.background-info-list-vintage')
75974                         .classed('hide', false)
75975                         .selectAll('.background-info-span-vintage')
75976                         .text(metadata.vintage);
75977
75978                     // update other metadata
75979                     metadataKeys.forEach(function(k) {
75980                         if (k === 'zoom' || k === 'vintage') { return; }  // done already
75981                         var val = result[k];
75982                         metadata[k] = val;
75983                         selection.selectAll('.background-info-list-' + k)
75984                             .classed('hide', !val)
75985                             .selectAll('.background-info-span-' + k)
75986                             .text(val);
75987                     });
75988                 });
75989             }
75990
75991
75992             var panel = function(selection) {
75993                 selection.call(redraw);
75994
75995                 context.map()
75996                     .on('drawn.info-background', function() {
75997                         selection.call(debouncedRedraw);
75998                     })
75999                     .on('move.info-background', function() {
76000                         selection.call(debouncedGetMetadata);
76001                     });
76002
76003             };
76004
76005             panel.off = function() {
76006                 context.map()
76007                     .on('drawn.info-background', null)
76008                     .on('move.info-background', null);
76009             };
76010
76011             panel.id = 'background';
76012             panel.title = _t('info_panels.background.title');
76013             panel.key = _t('info_panels.background.key');
76014
76015
76016             return panel;
76017         }
76018
76019         function uiPanelHistory(context) {
76020             var osm;
76021
76022             function displayTimestamp(timestamp) {
76023                 if (!timestamp) { return _t('info_panels.history.unknown'); }
76024                 var options = {
76025                     day: 'numeric', month: 'short', year: 'numeric',
76026                     hour: 'numeric', minute: 'numeric', second: 'numeric'
76027                 };
76028                 var d = new Date(timestamp);
76029                 if (isNaN(d.getTime())) { return _t('info_panels.history.unknown'); }
76030                 return d.toLocaleString(_mainLocalizer.localeCode(), options);
76031             }
76032
76033
76034             function displayUser(selection, userName) {
76035                 if (!userName) {
76036                     selection
76037                         .append('span')
76038                         .text(_t('info_panels.history.unknown'));
76039                     return;
76040                 }
76041
76042                 selection
76043                     .append('span')
76044                     .attr('class', 'user-name')
76045                     .text(userName);
76046
76047                 var links = selection
76048                     .append('div')
76049                     .attr('class', 'links');
76050
76051                 if (osm) {
76052                     links
76053                         .append('a')
76054                         .attr('class', 'user-osm-link')
76055                         .attr('href', osm.userURL(userName))
76056                         .attr('target', '_blank')
76057                         .attr('tabindex', -1)
76058                         .text('OSM');
76059                 }
76060
76061                 links
76062                     .append('a')
76063                     .attr('class', 'user-hdyc-link')
76064                     .attr('href', 'https://hdyc.neis-one.org/?' + userName)
76065                     .attr('target', '_blank')
76066                     .attr('tabindex', -1)
76067                     .text('HDYC');
76068             }
76069
76070
76071             function displayChangeset(selection, changeset) {
76072                 if (!changeset) {
76073                     selection
76074                         .append('span')
76075                         .text(_t('info_panels.history.unknown'));
76076                     return;
76077                 }
76078
76079                 selection
76080                     .append('span')
76081                     .attr('class', 'changeset-id')
76082                     .text(changeset);
76083
76084                 var links = selection
76085                     .append('div')
76086                     .attr('class', 'links');
76087
76088                 if (osm) {
76089                     links
76090                         .append('a')
76091                         .attr('class', 'changeset-osm-link')
76092                         .attr('href', osm.changesetURL(changeset))
76093                         .attr('target', '_blank')
76094                         .attr('tabindex', -1)
76095                         .text('OSM');
76096                 }
76097
76098                 links
76099                     .append('a')
76100                     .attr('class', 'changeset-osmcha-link')
76101                     .attr('href', 'https://osmcha.org/changesets/' + changeset)
76102                     .attr('target', '_blank')
76103                     .attr('tabindex', -1)
76104                     .text('OSMCha');
76105
76106                 links
76107                     .append('a')
76108                     .attr('class', 'changeset-achavi-link')
76109                     .attr('href', 'https://overpass-api.de/achavi/?changeset=' + changeset)
76110                     .attr('target', '_blank')
76111                     .attr('tabindex', -1)
76112                     .text('Achavi');
76113             }
76114
76115
76116             function redraw(selection) {
76117                 var selectedNoteID = context.selectedNoteID();
76118                 osm = context.connection();
76119
76120                 var selected, note, entity;
76121                 if (selectedNoteID && osm) {       // selected 1 note
76122                     selected = [ _t('note.note') + ' ' + selectedNoteID ];
76123                     note = osm.getNote(selectedNoteID);
76124                 } else {                           // selected 1..n entities
76125                     selected = context.selectedIDs()
76126                         .filter(function(e) { return context.hasEntity(e); });
76127                     if (selected.length) {
76128                         entity = context.entity(selected[0]);
76129                     }
76130                 }
76131
76132                 var singular = selected.length === 1 ? selected[0] : null;
76133
76134                 selection.html('');
76135
76136                 selection
76137                     .append('h4')
76138                     .attr('class', 'history-heading')
76139                     .text(singular || _t('info_panels.history.selected', { n: selected.length }));
76140
76141                 if (!singular) { return; }
76142
76143                 if (entity) {
76144                     selection.call(redrawEntity, entity);
76145                 } else if (note) {
76146                     selection.call(redrawNote, note);
76147                 }
76148             }
76149
76150
76151             function redrawNote(selection, note) {
76152                 if (!note || note.isNew()) {
76153                     selection
76154                         .append('div')
76155                         .text(_t('info_panels.history.note_no_history'));
76156                     return;
76157                 }
76158
76159                 var list = selection
76160                     .append('ul');
76161
76162                 list
76163                     .append('li')
76164                     .text(_t('info_panels.history.note_comments') + ':')
76165                     .append('span')
76166                     .text(note.comments.length);
76167
76168                 if (note.comments.length) {
76169                     list
76170                         .append('li')
76171                         .text(_t('info_panels.history.note_created_date') + ':')
76172                         .append('span')
76173                         .text(displayTimestamp(note.comments[0].date));
76174
76175                     list
76176                         .append('li')
76177                         .text(_t('info_panels.history.note_created_user') + ':')
76178                         .call(displayUser, note.comments[0].user);
76179                 }
76180
76181                 if (osm) {
76182                     selection
76183                         .append('a')
76184                         .attr('class', 'view-history-on-osm')
76185                         .attr('target', '_blank')
76186                         .attr('tabindex', -1)
76187                         .attr('href', osm.noteURL(note))
76188                         .call(svgIcon('#iD-icon-out-link', 'inline'))
76189                         .append('span')
76190                         .text(_t('info_panels.history.note_link_text'));
76191                 }
76192             }
76193
76194
76195             function redrawEntity(selection, entity) {
76196                 if (!entity || entity.isNew()) {
76197                     selection
76198                         .append('div')
76199                         .text(_t('info_panels.history.no_history'));
76200                     return;
76201                 }
76202
76203                 var links = selection
76204                     .append('div')
76205                     .attr('class', 'links');
76206
76207                 if (osm) {
76208                     links
76209                         .append('a')
76210                         .attr('class', 'view-history-on-osm')
76211                         .attr('href', osm.historyURL(entity))
76212                         .attr('target', '_blank')
76213                         .attr('tabindex', -1)
76214                         .attr('title', _t('info_panels.history.link_text'))
76215                         .text('OSM');
76216                 }
76217                 links
76218                     .append('a')
76219                     .attr('class', 'pewu-history-viewer-link')
76220                     .attr('href', 'https://pewu.github.io/osm-history/#/' + entity.type + '/' + entity.osmId())
76221                     .attr('target', '_blank')
76222                     .attr('tabindex', -1)
76223                     .text('PeWu');
76224
76225                 var list = selection
76226                     .append('ul');
76227
76228                 list
76229                     .append('li')
76230                     .text(_t('info_panels.history.version') + ':')
76231                     .append('span')
76232                     .text(entity.version);
76233
76234                 list
76235                     .append('li')
76236                     .text(_t('info_panels.history.last_edit') + ':')
76237                     .append('span')
76238                     .text(displayTimestamp(entity.timestamp));
76239
76240                 list
76241                     .append('li')
76242                     .text(_t('info_panels.history.edited_by') + ':')
76243                     .call(displayUser, entity.user);
76244
76245                 list
76246                     .append('li')
76247                     .text(_t('info_panels.history.changeset') + ':')
76248                     .call(displayChangeset, entity.changeset);
76249             }
76250
76251
76252             var panel = function(selection) {
76253                 selection.call(redraw);
76254
76255                 context.map()
76256                     .on('drawn.info-history', function() {
76257                         selection.call(redraw);
76258                     });
76259
76260                 context
76261                     .on('enter.info-history', function() {
76262                         selection.call(redraw);
76263                     });
76264             };
76265
76266             panel.off = function() {
76267                 context.map().on('drawn.info-history', null);
76268                 context.on('enter.info-history', null);
76269             };
76270
76271             panel.id = 'history';
76272             panel.title = _t('info_panels.history.title');
76273             panel.key = _t('info_panels.history.key');
76274
76275
76276             return panel;
76277         }
76278
76279         var OSM_PRECISION = 7;
76280
76281         /**
76282          * Returns a localized representation of the given length measurement.
76283          *
76284          * @param {Number} m area in meters
76285          * @param {Boolean} isImperial true for U.S. customary units; false for metric
76286          */
76287         function displayLength(m, isImperial) {
76288             var d = m * (isImperial ? 3.28084 : 1);
76289             var unit;
76290
76291             if (isImperial) {
76292                 if (d >= 5280) {
76293                     d /= 5280;
76294                     unit = 'miles';
76295                 } else {
76296                     unit = 'feet';
76297                 }
76298             } else {
76299                 if (d >= 1000) {
76300                     d /= 1000;
76301                     unit = 'kilometers';
76302                 } else {
76303                     unit = 'meters';
76304                 }
76305             }
76306
76307             return _t('units.' + unit, {
76308                 quantity: d.toLocaleString(_mainLocalizer.localeCode(), {
76309                     maximumSignificantDigits: 4
76310                 })
76311             });
76312         }
76313
76314         /**
76315          * Returns a localized representation of the given area measurement.
76316          *
76317          * @param {Number} m2 area in square meters
76318          * @param {Boolean} isImperial true for U.S. customary units; false for metric
76319          */
76320         function displayArea(m2, isImperial) {
76321             var locale = _mainLocalizer.localeCode();
76322             var d = m2 * (isImperial ? 10.7639111056 : 1);
76323             var d1, d2, area;
76324             var unit1 = '';
76325             var unit2 = '';
76326
76327             if (isImperial) {
76328                 if (d >= 6969600) { // > 0.25mi² show mi²
76329                     d1 = d / 27878400;
76330                     unit1 = 'square_miles';
76331                 } else {
76332                     d1 = d;
76333                     unit1 = 'square_feet';
76334                 }
76335
76336                 if (d > 4356 && d < 43560000) { // 0.1 - 1000 acres
76337                     d2 = d / 43560;
76338                     unit2 = 'acres';
76339                 }
76340
76341             } else {
76342                 if (d >= 250000) { // > 0.25km² show km²
76343                     d1 = d / 1000000;
76344                     unit1 = 'square_kilometers';
76345                 } else {
76346                     d1 = d;
76347                     unit1 = 'square_meters';
76348                 }
76349
76350                 if (d > 1000 && d < 10000000) { // 0.1 - 1000 hectares
76351                     d2 = d / 10000;
76352                     unit2 = 'hectares';
76353                 }
76354             }
76355
76356             area = _t('units.' + unit1, {
76357                 quantity: d1.toLocaleString(locale, {
76358                     maximumSignificantDigits: 4
76359                 })
76360             });
76361
76362             if (d2) {
76363                 return _t('units.area_pair', {
76364                     area1: area,
76365                     area2: _t('units.' + unit2, {
76366                         quantity: d2.toLocaleString(locale, {
76367                             maximumSignificantDigits: 2
76368                         })
76369                     })
76370                 });
76371             } else {
76372                 return area;
76373             }
76374         }
76375
76376         function wrap(x, min, max) {
76377             var d = max - min;
76378             return ((x - min) % d + d) % d + min;
76379         }
76380
76381         function clamp$1(x, min, max) {
76382             return Math.max(min, Math.min(x, max));
76383         }
76384
76385         function displayCoordinate(deg, pos, neg) {
76386             var locale = _mainLocalizer.localeCode();
76387             var min = (Math.abs(deg) - Math.floor(Math.abs(deg))) * 60;
76388             var sec = (min - Math.floor(min)) * 60;
76389             var displayDegrees = _t('units.arcdegrees', {
76390                 quantity: Math.floor(Math.abs(deg)).toLocaleString(locale)
76391             });
76392             var displayCoordinate;
76393
76394             if (Math.floor(sec) > 0) {
76395                 displayCoordinate = displayDegrees +
76396                     _t('units.arcminutes', {
76397                         quantity: Math.floor(min).toLocaleString(locale)
76398                     }) +
76399                     _t('units.arcseconds', {
76400                         quantity: Math.round(sec).toLocaleString(locale)
76401                     });
76402             } else if (Math.floor(min) > 0) {
76403                 displayCoordinate = displayDegrees +
76404                     _t('units.arcminutes', {
76405                         quantity: Math.round(min).toLocaleString(locale)
76406                     });
76407             } else {
76408                 displayCoordinate = _t('units.arcdegrees', {
76409                     quantity: Math.round(Math.abs(deg)).toLocaleString(locale)
76410                 });
76411             }
76412
76413             if (deg === 0) {
76414                 return displayCoordinate;
76415             } else {
76416                 return _t('units.coordinate', {
76417                     coordinate: displayCoordinate,
76418                     direction: _t('units.' + (deg > 0 ? pos : neg))
76419                 });
76420             }
76421         }
76422
76423         /**
76424          * Returns given coordinate pair in degree-minute-second format.
76425          *
76426          * @param {Array<Number>} coord longitude and latitude
76427          */
76428         function dmsCoordinatePair(coord) {
76429             return _t('units.coordinate_pair', {
76430                 latitude: displayCoordinate(clamp$1(coord[1], -90, 90), 'north', 'south'),
76431                 longitude: displayCoordinate(wrap(coord[0], -180, 180), 'east', 'west')
76432             });
76433         }
76434
76435         /**
76436          * Returns the given coordinate pair in decimal format.
76437          * note: unlocalized to avoid comma ambiguity - see #4765
76438          *
76439          * @param {Array<Number>} coord longitude and latitude
76440          */
76441         function decimalCoordinatePair(coord) {
76442             return _t('units.coordinate_pair', {
76443                 latitude: clamp$1(coord[1], -90, 90).toFixed(OSM_PRECISION),
76444                 longitude: wrap(coord[0], -180, 180).toFixed(OSM_PRECISION)
76445             });
76446         }
76447
76448         function uiPanelLocation(context) {
76449             var currLocation = '';
76450
76451
76452             function redraw(selection) {
76453                 selection.html('');
76454
76455                 var list = selection
76456                     .append('ul');
76457
76458                 // Mouse coordinates
76459                 var coord = context.map().mouseCoordinates();
76460                 if (coord.some(isNaN)) {
76461                     coord = context.map().center();
76462                 }
76463
76464                 list
76465                     .append('li')
76466                     .text(dmsCoordinatePair(coord))
76467                     .append('li')
76468                     .text(decimalCoordinatePair(coord));
76469
76470                 // Location Info
76471                 selection
76472                     .append('div')
76473                     .attr('class', 'location-info')
76474                     .text(currLocation || ' ');
76475
76476                 debouncedGetLocation(selection, coord);
76477             }
76478
76479
76480             var debouncedGetLocation = debounce(getLocation, 250);
76481             function getLocation(selection, coord) {
76482                 if (!services.geocoder) {
76483                     currLocation = _t('info_panels.location.unknown_location');
76484                     selection.selectAll('.location-info')
76485                         .text(currLocation);
76486                 } else {
76487                     services.geocoder.reverse(coord, function(err, result) {
76488                         currLocation = result ? result.display_name : _t('info_panels.location.unknown_location');
76489                         selection.selectAll('.location-info')
76490                             .text(currLocation);
76491                     });
76492                 }
76493             }
76494
76495
76496             var panel = function(selection) {
76497                 selection.call(redraw);
76498
76499                 context.surface()
76500                     .on(('PointerEvent' in window ? 'pointer' : 'mouse') + 'move.info-location', function() {
76501                         selection.call(redraw);
76502                     });
76503             };
76504
76505             panel.off = function() {
76506                 context.surface()
76507                     .on('.info-location', null);
76508             };
76509
76510             panel.id = 'location';
76511             panel.title = _t('info_panels.location.title');
76512             panel.key = _t('info_panels.location.key');
76513
76514
76515             return panel;
76516         }
76517
76518         function uiPanelMeasurement(context) {
76519             var locale = _mainLocalizer.localeCode();
76520             var isImperial = !_mainLocalizer.usesMetric();
76521
76522
76523             function radiansToMeters(r) {
76524                 // using WGS84 authalic radius (6371007.1809 m)
76525                 return r * 6371007.1809;
76526             }
76527
76528             function steradiansToSqmeters(r) {
76529                 // http://gis.stackexchange.com/a/124857/40446
76530                 return r / (4 * Math.PI) * 510065621724000;
76531             }
76532
76533
76534             function toLineString(feature) {
76535                 if (feature.type === 'LineString') { return feature; }
76536
76537                 var result = { type: 'LineString', coordinates: [] };
76538                 if (feature.type === 'Polygon') {
76539                     result.coordinates = feature.coordinates[0];
76540                 } else if (feature.type === 'MultiPolygon') {
76541                     result.coordinates = feature.coordinates[0][0];
76542                 }
76543
76544                 return result;
76545             }
76546
76547
76548             function redraw(selection) {
76549                 var graph = context.graph();
76550                 var selectedNoteID = context.selectedNoteID();
76551                 var osm = services.osm;
76552
76553                 var heading;
76554                 var center, location, centroid;
76555                 var closed, geometry;
76556                 var totalNodeCount, length = 0, area = 0;
76557
76558                 if (selectedNoteID && osm) {       // selected 1 note
76559
76560                     var note = osm.getNote(selectedNoteID);
76561                     heading = _t('note.note') + ' ' + selectedNoteID;
76562                     location = note.loc;
76563                     geometry = 'note';
76564
76565                 } else {                           // selected 1..n entities
76566                     var selectedIDs = context.selectedIDs().filter(function(id) {
76567                         return context.hasEntity(id);
76568                     });
76569                     var selected = selectedIDs.map(function(id) {
76570                         return context.entity(id);
76571                     });
76572
76573                     heading = selected.length === 1 ? selected[0].id :
76574                         _t('info_panels.measurement.selected', { n: selected.length.toLocaleString(locale) });
76575
76576                     if (selected.length) {
76577                         var extent = geoExtent();
76578                         for (var i in selected) {
76579                             var entity = selected[i];
76580                             extent._extend(entity.extent(graph));
76581
76582                             geometry = entity.geometry(graph);
76583                             if (geometry === 'line' || geometry === 'area') {
76584                                 closed = (entity.type === 'relation') || (entity.isClosed() && !entity.isDegenerate());
76585                                 var feature = entity.asGeoJSON(graph);
76586                                 length += radiansToMeters(d3_geoLength(toLineString(feature)));
76587                                 centroid = d3_geoCentroid(feature);
76588                                 if (closed) {
76589                                     area += steradiansToSqmeters(entity.area(graph));
76590                                 }
76591                             }
76592                         }
76593
76594                         if (selected.length > 1) {
76595                             geometry = null;
76596                             closed = null;
76597                             centroid = null;
76598                         }
76599
76600                         if (selected.length === 1 && selected[0].type === 'node') {
76601                             location = selected[0].loc;
76602                         } else {
76603                             totalNodeCount = utilGetAllNodes(selectedIDs, context.graph()).length;
76604                         }
76605
76606                         if (!location && !centroid) {
76607                             center = extent.center();
76608                         }
76609                     }
76610                 }
76611
76612                 selection.html('');
76613
76614                 if (heading) {
76615                     selection
76616                         .append('h4')
76617                         .attr('class', 'measurement-heading')
76618                         .text(heading);
76619                 }
76620
76621                 var list = selection
76622                     .append('ul');
76623                 var coordItem;
76624
76625                 if (geometry) {
76626                     list
76627                         .append('li')
76628                         .text(_t('info_panels.measurement.geometry') + ':')
76629                         .append('span')
76630                         .text(
76631                             closed ? _t('info_panels.measurement.closed_' + geometry) : _t('geometry.' + geometry)
76632                         );
76633                 }
76634
76635                 if (totalNodeCount) {
76636                     list
76637                         .append('li')
76638                         .text(_t('info_panels.measurement.node_count') + ':')
76639                         .append('span')
76640                         .text(totalNodeCount.toLocaleString(locale));
76641                 }
76642
76643                 if (area) {
76644                     list
76645                         .append('li')
76646                         .text(_t('info_panels.measurement.area') + ':')
76647                         .append('span')
76648                         .text(displayArea(area, isImperial));
76649                 }
76650
76651                 if (length) {
76652                     var lengthLabel = _t('info_panels.measurement.' + (closed ? 'perimeter' : 'length'));
76653                     list
76654                         .append('li')
76655                         .text(lengthLabel + ':')
76656                         .append('span')
76657                         .text(displayLength(length, isImperial));
76658                 }
76659
76660                 if (location) {
76661                     coordItem = list
76662                         .append('li')
76663                         .text(_t('info_panels.measurement.location') + ':');
76664                     coordItem.append('span')
76665                         .text(dmsCoordinatePair(location));
76666                     coordItem.append('span')
76667                         .text(decimalCoordinatePair(location));
76668                 }
76669
76670                 if (centroid) {
76671                     coordItem = list
76672                         .append('li')
76673                         .text(_t('info_panels.measurement.centroid') + ':');
76674                     coordItem.append('span')
76675                         .text(dmsCoordinatePair(centroid));
76676                     coordItem.append('span')
76677                         .text(decimalCoordinatePair(centroid));
76678                 }
76679
76680                 if (center) {
76681                     coordItem = list
76682                         .append('li')
76683                         .text(_t('info_panels.measurement.center') + ':');
76684                     coordItem.append('span')
76685                         .text(dmsCoordinatePair(center));
76686                     coordItem.append('span')
76687                         .text(decimalCoordinatePair(center));
76688                 }
76689
76690                 if (length || area) {
76691                     var toggle  = isImperial ? 'imperial' : 'metric';
76692                     selection
76693                         .append('a')
76694                         .text(_t('info_panels.measurement.' + toggle))
76695                         .attr('href', '#')
76696                         .attr('class', 'button button-toggle-units')
76697                         .on('click', function() {
76698                             event.preventDefault();
76699                             isImperial = !isImperial;
76700                             selection.call(redraw);
76701                         });
76702                 }
76703             }
76704
76705
76706             var panel = function(selection) {
76707                 selection.call(redraw);
76708
76709                 context.map()
76710                     .on('drawn.info-measurement', function() {
76711                         selection.call(redraw);
76712                     });
76713
76714                 context
76715                     .on('enter.info-measurement', function() {
76716                         selection.call(redraw);
76717                     });
76718             };
76719
76720             panel.off = function() {
76721                 context.map().on('drawn.info-measurement', null);
76722                 context.on('enter.info-measurement', null);
76723             };
76724
76725             panel.id = 'measurement';
76726             panel.title = _t('info_panels.measurement.title');
76727             panel.key = _t('info_panels.measurement.key');
76728
76729
76730             return panel;
76731         }
76732
76733         var uiInfoPanels = {
76734             background: uiPanelBackground,
76735             history: uiPanelHistory,
76736             location: uiPanelLocation,
76737             measurement: uiPanelMeasurement,
76738         };
76739
76740         function uiInfo(context) {
76741             var ids = Object.keys(uiInfoPanels);
76742             var wasActive = ['measurement'];
76743             var panels = {};
76744             var active = {};
76745
76746             // create panels
76747             ids.forEach(function(k) {
76748                 if (!panels[k]) {
76749                     panels[k] = uiInfoPanels[k](context);
76750                     active[k] = false;
76751                 }
76752             });
76753
76754
76755             function info(selection) {
76756
76757                 function redraw() {
76758                     var activeids = ids.filter(function(k) { return active[k]; }).sort();
76759
76760                     var containers = infoPanels.selectAll('.panel-container')
76761                         .data(activeids, function(k) { return k; });
76762
76763                     containers.exit()
76764                         .style('opacity', 1)
76765                         .transition()
76766                         .duration(200)
76767                         .style('opacity', 0)
76768                         .on('end', function(d) {
76769                             select(this)
76770                                 .call(panels[d].off)
76771                                 .remove();
76772                         });
76773
76774                     var enter = containers.enter()
76775                         .append('div')
76776                         .attr('class', function(d) { return 'fillD2 panel-container panel-container-' + d; });
76777
76778                     enter
76779                         .style('opacity', 0)
76780                         .transition()
76781                         .duration(200)
76782                         .style('opacity', 1);
76783
76784                     var title = enter
76785                         .append('div')
76786                         .attr('class', 'panel-title fillD2');
76787
76788                     title
76789                         .append('h3')
76790                         .text(function(d) { return panels[d].title; });
76791
76792                     title
76793                         .append('button')
76794                         .attr('class', 'close')
76795                         .on('click', function (d) { info.toggle(d); })
76796                         .call(svgIcon('#iD-icon-close'));
76797
76798                     enter
76799                         .append('div')
76800                         .attr('class', function(d) { return 'panel-content panel-content-' + d; });
76801
76802
76803                     // redraw the panels
76804                     infoPanels.selectAll('.panel-content')
76805                         .each(function(d) {
76806                             select(this).call(panels[d]);
76807                         });
76808                 }
76809
76810
76811                 info.toggle = function(which) {
76812                     if (event) {
76813                         event.stopImmediatePropagation();
76814                         event.preventDefault();
76815                     }
76816
76817                     var activeids = ids.filter(function(k) { return active[k]; });
76818
76819                     if (which) {  // toggle one
76820                         active[which] = !active[which];
76821                         if (activeids.length === 1 && activeids[0] === which) {  // none active anymore
76822                             wasActive = [which];
76823                         }
76824
76825                         context.container().select('.' + which + '-panel-toggle-item')
76826                             .classed('active', active[which])
76827                             .select('input')
76828                             .property('checked', active[which]);
76829
76830                     } else {      // toggle all
76831                         if (activeids.length) {
76832                             wasActive = activeids;
76833                             activeids.forEach(function(k) { active[k] = false; });
76834                         } else {
76835                             wasActive.forEach(function(k) { active[k] = true; });
76836                         }
76837                     }
76838
76839                     redraw();
76840                 };
76841
76842
76843                 var infoPanels = selection.selectAll('.info-panels')
76844                     .data([0]);
76845
76846                 infoPanels = infoPanels.enter()
76847                     .append('div')
76848                     .attr('class', 'info-panels')
76849                     .merge(infoPanels);
76850
76851                 redraw();
76852
76853                 context.keybinding()
76854                     .on(uiCmd('⌘' + _t('info_panels.key')), info.toggle);
76855
76856                 ids.forEach(function(k) {
76857                     var key = _t('info_panels.' + k + '.key', { default: null });
76858                     if (!key) { return; }
76859                     context.keybinding()
76860                         .on(uiCmd('⌘⇧' + key), function() { info.toggle(k); });
76861                 });
76862             }
76863
76864             return info;
76865         }
76866
76867         function pointBox(loc, context) {
76868             var rect = context.surfaceRect();
76869             var point = context.curtainProjection(loc);
76870             return {
76871                 left: point[0] + rect.left - 40,
76872                 top: point[1] + rect.top - 60,
76873                 width: 80,
76874                 height: 90
76875             };
76876         }
76877
76878
76879         function pad(locOrBox, padding, context) {
76880             var box;
76881             if (locOrBox instanceof Array) {
76882                 var rect = context.surfaceRect();
76883                 var point = context.curtainProjection(locOrBox);
76884                 box = {
76885                     left: point[0] + rect.left,
76886                     top: point[1] + rect.top
76887                 };
76888             } else {
76889                 box = locOrBox;
76890             }
76891
76892             return {
76893                 left: box.left - padding,
76894                 top: box.top - padding,
76895                 width: (box.width || 0) + 2 * padding,
76896                 height: (box.width || 0) + 2 * padding
76897             };
76898         }
76899
76900
76901         function icon(name, svgklass, useklass) {
76902             return '<svg class="icon ' + (svgklass || '') + '">' +
76903                  '<use xlink:href="' + name + '"' +
76904                  (useklass ? ' class="' + useklass + '"' : '') + '></use></svg>';
76905         }
76906
76907         var helpStringReplacements;
76908
76909         // Returns the localized string for `id` with a standardized set of icon, key, and
76910         // label replacements suitable for tutorials and documentation. Optionally supplemented
76911         // with custom `replacements`
76912         function helpString(id, replacements) {
76913             // only load these the first time
76914             if (!helpStringReplacements) { helpStringReplacements = {
76915                 // insert icons corresponding to various UI elements
76916                 point_icon: icon('#iD-icon-point', 'pre-text'),
76917                 line_icon: icon('#iD-icon-line', 'pre-text'),
76918                 area_icon: icon('#iD-icon-area', 'pre-text'),
76919                 note_icon: icon('#iD-icon-note', 'pre-text add-note'),
76920                 plus: icon('#iD-icon-plus', 'pre-text'),
76921                 minus: icon('#iD-icon-minus', 'pre-text'),
76922                 move_icon: icon('#iD-operation-move', 'pre-text operation'),
76923                 merge_icon: icon('#iD-operation-merge', 'pre-text operation'),
76924                 delete_icon: icon('#iD-operation-delete', 'pre-text operation'),
76925                 circularize_icon: icon('#iD-operation-circularize', 'pre-text operation'),
76926                 split_icon: icon('#iD-operation-split', 'pre-text operation'),
76927                 orthogonalize_icon: icon('#iD-operation-orthogonalize', 'pre-text operation'),
76928                 disconnect_icon: icon('#iD-operation-disconnect', 'pre-text operation'),
76929                 layers_icon: icon('#iD-icon-layers', 'pre-text'),
76930                 data_icon: icon('#iD-icon-data', 'pre-text'),
76931                 inspect: icon('#iD-icon-inspect', 'pre-text'),
76932                 help_icon: icon('#iD-icon-help', 'pre-text'),
76933                 undo_icon: icon(_mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-redo' : '#iD-icon-undo', 'pre-text'),
76934                 redo_icon: icon(_mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-undo' : '#iD-icon-redo', 'pre-text'),
76935                 save_icon: icon('#iD-icon-save', 'pre-text'),
76936                 leftclick: icon('#iD-walkthrough-mouse-left', 'pre-text operation'),
76937                 rightclick: icon('#iD-walkthrough-mouse-right', 'pre-text operation'),
76938                 mousewheel_icon: icon('#iD-walkthrough-mousewheel', 'pre-text operation'),
76939                 tap_icon: icon('#iD-walkthrough-tap', 'pre-text operation'),
76940                 doubletap_icon: icon('#iD-walkthrough-doubletap', 'pre-text operation'),
76941                 longpress_icon: icon('#iD-walkthrough-longpress', 'pre-text operation'),
76942                 touchdrag_icon: icon('#iD-walkthrough-touchdrag', 'pre-text operation'),
76943                 pinch_icon: icon('#iD-walkthrough-pinch-apart', 'pre-text operation'),
76944
76945                 // insert keys; may be localized and platform-dependent
76946                 shift: uiCmd.display('⇧'),
76947                 alt: uiCmd.display('⌥'),
76948                 return: uiCmd.display('↵'),
76949                 esc: _t('shortcuts.key.esc'),
76950                 space: _t('shortcuts.key.space'),
76951                 add_note_key: _t('modes.add_note.key'),
76952                 help_key: _t('help.key'),
76953                 shortcuts_key: _t('shortcuts.toggle.key'),
76954
76955                 // reference localized UI labels directly so that they'll always match
76956                 save: _t('save.title'),
76957                 undo: _t('undo.title'),
76958                 redo: _t('redo.title'),
76959                 upload: _t('commit.save'),
76960                 point: _t('modes.add_point.title'),
76961                 line: _t('modes.add_line.title'),
76962                 area: _t('modes.add_area.title'),
76963                 note: _t('modes.add_note.label'),
76964                 delete: _t('operations.delete.title'),
76965                 move: _t('operations.move.title'),
76966                 orthogonalize: _t('operations.orthogonalize.title'),
76967                 circularize: _t('operations.circularize.title'),
76968                 merge: _t('operations.merge.title'),
76969                 disconnect: _t('operations.disconnect.title'),
76970                 split: _t('operations.split.title'),
76971                 map_data: _t('map_data.title'),
76972                 osm_notes: _t('map_data.layers.notes.title'),
76973                 fields: _t('inspector.fields'),
76974                 tags: _t('inspector.tags'),
76975                 relations: _t('inspector.relations'),
76976                 new_relation: _t('inspector.new_relation'),
76977                 turn_restrictions: _t('presets.fields.restrictions.label'),
76978                 background_settings: _t('background.description'),
76979                 imagery_offset: _t('background.fix_misalignment'),
76980                 start_the_walkthrough: _t('splash.walkthrough'),
76981                 help: _t('help.title'),
76982                 ok: _t('intro.ok')
76983             }; }
76984
76985             var reps;
76986             if (replacements) {
76987                 reps = Object.assign(replacements, helpStringReplacements);
76988             } else {
76989                 reps = helpStringReplacements;
76990             }
76991
76992             return _t(id, reps)
76993                  // use keyboard key styling for shortcuts
76994                 .replace(/\`(.*?)\`/g, '<kbd>$1</kbd>');
76995         }
76996
76997
76998         function slugify(text) {
76999             return text.toString().toLowerCase()
77000                 .replace(/\s+/g, '-')           // Replace spaces with -
77001                 .replace(/[^\w\-]+/g, '')       // Remove all non-word chars
77002                 .replace(/\-\-+/g, '-')         // Replace multiple - with single -
77003                 .replace(/^-+/, '')             // Trim - from start of text
77004                 .replace(/-+$/, '');            // Trim - from end of text
77005         }
77006
77007
77008         // console warning for missing walkthrough names
77009         var missingStrings = {};
77010         function checkKey(key, text) {
77011             if (_t(key, { default: undefined}) === undefined) {
77012                 if (missingStrings.hasOwnProperty(key)) { return; }  // warn once
77013                 missingStrings[key] = text;
77014                 var missing = key + ': ' + text;
77015                 if (typeof console !== 'undefined') { console.log(missing); } // eslint-disable-line
77016             }
77017         }
77018
77019
77020         function localize(obj) {
77021             var key;
77022
77023             // Assign name if entity has one..
77024             var name = obj.tags && obj.tags.name;
77025             if (name) {
77026                 key = 'intro.graph.name.' + slugify(name);
77027                 obj.tags.name = _t(key, { default: name });
77028                 checkKey(key, name);
77029             }
77030
77031             // Assign street name if entity has one..
77032             var street = obj.tags && obj.tags['addr:street'];
77033             if (street) {
77034                 key = 'intro.graph.name.' + slugify(street);
77035                 obj.tags['addr:street'] = _t(key, { default: street });
77036                 checkKey(key, street);
77037
77038                 // Add address details common across walkthrough..
77039                 var addrTags = [
77040                     'block_number', 'city', 'county', 'district', 'hamlet', 'neighbourhood',
77041                     'postcode', 'province', 'quarter', 'state', 'subdistrict', 'suburb'
77042                 ];
77043                 addrTags.forEach(function(k) {
77044                     var key = 'intro.graph.' + k;
77045                     var tag = 'addr:' + k;
77046                     var val = obj.tags && obj.tags[tag];
77047                     var str = _t(key, { default: val });
77048
77049                     if (str) {
77050                         if (str.match(/^<.*>$/) !== null) {
77051                             delete obj.tags[tag];
77052                         } else {
77053                             obj.tags[tag] = str;
77054                         }
77055                     }
77056                 });
77057             }
77058
77059             return obj;
77060         }
77061
77062
77063         // Used to detect squareness.. some duplicataion of code from actionOrthogonalize.
77064         function isMostlySquare(points) {
77065             // note: uses 15 here instead of the 12 from actionOrthogonalize because
77066             // actionOrthogonalize can actually straighten some larger angles as it iterates
77067             var threshold = 15; // degrees within right or straight
77068             var lowerBound = Math.cos((90 - threshold) * Math.PI / 180);  // near right
77069             var upperBound = Math.cos(threshold * Math.PI / 180);         // near straight
77070
77071             for (var i = 0; i < points.length; i++) {
77072                 var a = points[(i - 1 + points.length) % points.length];
77073                 var origin = points[i];
77074                 var b = points[(i + 1) % points.length];
77075
77076                 var dotp = geoVecNormalizedDot(a, b, origin);
77077                 var mag = Math.abs(dotp);
77078                 if (mag > lowerBound && mag < upperBound) {
77079                     return false;
77080                 }
77081             }
77082
77083             return true;
77084         }
77085
77086
77087         function selectMenuItem(context, operation) {
77088             return context.container().select('.edit-menu .edit-menu-item-' + operation);
77089         }
77090
77091
77092         function transitionTime(point1, point2) {
77093             var distance = geoSphericalDistance(point1, point2);
77094             if (distance === 0)
77095                 { return 0; }
77096             else if (distance < 80)
77097                 { return 500; }
77098             else
77099                 { return 1000; }
77100         }
77101
77102         // Tooltips and svg mask used to highlight certain features
77103         function uiCurtain(containerNode) {
77104
77105             var surface = select(null),
77106                 tooltip = select(null),
77107                 darkness = select(null);
77108
77109             function curtain(selection) {
77110                 surface = selection
77111                     .append('svg')
77112                     .attr('class', 'curtain')
77113                     .style('top', 0)
77114                     .style('left', 0);
77115
77116                 darkness = surface.append('path')
77117                     .attr('x', 0)
77118                     .attr('y', 0)
77119                     .attr('class', 'curtain-darkness');
77120
77121                 select(window).on('resize.curtain', resize);
77122
77123                 tooltip = selection.append('div')
77124                     .attr('class', 'tooltip');
77125
77126                 tooltip
77127                     .append('div')
77128                     .attr('class', 'popover-arrow');
77129
77130                 tooltip
77131                     .append('div')
77132                     .attr('class', 'popover-inner');
77133
77134                 resize();
77135
77136
77137                 function resize() {
77138                     surface
77139                         .attr('width', containerNode.clientWidth)
77140                         .attr('height', containerNode.clientHeight);
77141                     curtain.cut(darkness.datum());
77142                 }
77143             }
77144
77145
77146             /**
77147              * Reveal cuts the curtain to highlight the given box,
77148              * and shows a tooltip with instructions next to the box.
77149              *
77150              * @param  {String|ClientRect} [box]   box used to cut the curtain
77151              * @param  {String}    [text]          text for a tooltip
77152              * @param  {Object}    [options]
77153              * @param  {string}    [options.tooltipClass]    optional class to add to the tooltip
77154              * @param  {integer}   [options.duration]        transition time in milliseconds
77155              * @param  {string}    [options.buttonText]      if set, create a button with this text label
77156              * @param  {function}  [options.buttonCallback]  if set, the callback for the button
77157              * @param  {function}  [options.padding]         extra margin in px to put around bbox
77158              * @param  {String|ClientRect} [options.tooltipBox]  box for tooltip position, if different from box for the curtain
77159              */
77160             curtain.reveal = function(box, text, options) {
77161                 options = options || {};
77162
77163                 if (typeof box === 'string') {
77164                     box = select(box).node();
77165                 }
77166                 if (box && box.getBoundingClientRect) {
77167                     box = copyBox(box.getBoundingClientRect());
77168                     var containerRect = containerNode.getBoundingClientRect();
77169                     box.top -= containerRect.top;
77170                     box.left -= containerRect.left;
77171                 }
77172                 if (box && options.padding) {
77173                     box.top -= options.padding;
77174                     box.left -= options.padding;
77175                     box.bottom += options.padding;
77176                     box.right += options.padding;
77177                     box.height += options.padding * 2;
77178                     box.width += options.padding * 2;
77179                 }
77180
77181                 var tooltipBox;
77182                 if (options.tooltipBox) {
77183                     tooltipBox = options.tooltipBox;
77184                     if (typeof tooltipBox === 'string') {
77185                         tooltipBox = select(tooltipBox).node();
77186                     }
77187                     if (tooltipBox && tooltipBox.getBoundingClientRect) {
77188                         tooltipBox = copyBox(tooltipBox.getBoundingClientRect());
77189                     }
77190                 } else {
77191                     tooltipBox = box;
77192                 }
77193
77194                 if (tooltipBox && text) {
77195                     // pseudo markdown bold text for the instruction section..
77196                     var parts = text.split('**');
77197                     var html = parts[0] ? '<span>' + parts[0] + '</span>' : '';
77198                     if (parts[1]) {
77199                         html += '<span class="instruction">' + parts[1] + '</span>';
77200                     }
77201
77202                     html = html.replace(/\*(.*?)\*/g, '<em>$1</em>');   // emphasis
77203                     html = html.replace(/\{br\}/g, '<br/><br/>');       // linebreak
77204
77205                     if (options.buttonText && options.buttonCallback) {
77206                         html += '<div class="button-section">' +
77207                             '<button href="#" class="button action">' + options.buttonText + '</button></div>';
77208                     }
77209
77210                     var classes = 'curtain-tooltip popover tooltip arrowed in ' + (options.tooltipClass || '');
77211                     tooltip
77212                         .classed(classes, true)
77213                         .selectAll('.popover-inner')
77214                         .html(html);
77215
77216                     if (options.buttonText && options.buttonCallback) {
77217                         var button = tooltip.selectAll('.button-section .button.action');
77218                         button
77219                             .on('click', function() {
77220                                 event.preventDefault();
77221                                 options.buttonCallback();
77222                             });
77223                     }
77224
77225                     var tip = copyBox(tooltip.node().getBoundingClientRect()),
77226                         w = containerNode.clientWidth,
77227                         h = containerNode.clientHeight,
77228                         tooltipWidth = 200,
77229                         tooltipArrow = 5,
77230                         side, pos;
77231
77232
77233                     // hack: this will have bottom placement,
77234                     // so need to reserve extra space for the tooltip illustration.
77235                     if (options.tooltipClass === 'intro-mouse') {
77236                         tip.height += 80;
77237                     }
77238
77239                     // trim box dimensions to just the portion that fits in the container..
77240                     if (tooltipBox.top + tooltipBox.height > h) {
77241                         tooltipBox.height -= (tooltipBox.top + tooltipBox.height - h);
77242                     }
77243                     if (tooltipBox.left + tooltipBox.width > w) {
77244                         tooltipBox.width -= (tooltipBox.left + tooltipBox.width - w);
77245                     }
77246
77247                     // determine tooltip placement..
77248
77249                     if (tooltipBox.top + tooltipBox.height < 100) {
77250                         // tooltip below box..
77251                         side = 'bottom';
77252                         pos = [
77253                             tooltipBox.left + tooltipBox.width / 2 - tip.width / 2,
77254                             tooltipBox.top + tooltipBox.height
77255                         ];
77256
77257                     } else if (tooltipBox.top > h - 140) {
77258                         // tooltip above box..
77259                         side = 'top';
77260                         pos = [
77261                             tooltipBox.left + tooltipBox.width / 2 - tip.width / 2,
77262                             tooltipBox.top - tip.height
77263                         ];
77264
77265                     } else {
77266                         // tooltip to the side of the tooltipBox..
77267                         var tipY = tooltipBox.top + tooltipBox.height / 2 - tip.height / 2;
77268
77269                         if (_mainLocalizer.textDirection() === 'rtl') {
77270                             if (tooltipBox.left - tooltipWidth - tooltipArrow < 70) {
77271                                 side = 'right';
77272                                 pos = [tooltipBox.left + tooltipBox.width + tooltipArrow, tipY];
77273
77274                             } else {
77275                                 side = 'left';
77276                                 pos = [tooltipBox.left - tooltipWidth - tooltipArrow, tipY];
77277                             }
77278
77279                         } else {
77280                             if (tooltipBox.left + tooltipBox.width + tooltipArrow + tooltipWidth > w - 70) {
77281                                 side = 'left';
77282                                 pos = [tooltipBox.left - tooltipWidth - tooltipArrow, tipY];
77283                             }
77284                             else {
77285                                 side = 'right';
77286                                 pos = [tooltipBox.left + tooltipBox.width + tooltipArrow, tipY];
77287                             }
77288                         }
77289                     }
77290
77291                     if (options.duration !== 0 || !tooltip.classed(side)) {
77292                         tooltip.call(uiToggle(true));
77293                     }
77294
77295                     tooltip
77296                         .style('top', pos[1] + 'px')
77297                         .style('left', pos[0] + 'px')
77298                         .attr('class', classes + ' ' + side);
77299
77300
77301                     // shift popover-inner if it is very close to the top or bottom edge
77302                     // (doesn't affect the placement of the popover-arrow)
77303                     var shiftY = 0;
77304                     if (side === 'left' || side === 'right') {
77305                         if (pos[1] < 60) {
77306                             shiftY = 60 - pos[1];
77307                         }
77308                         else if (pos[1] + tip.height > h - 100) {
77309                             shiftY = h - pos[1] - tip.height - 100;
77310                         }
77311                     }
77312                     tooltip.selectAll('.popover-inner')
77313                         .style('top', shiftY + 'px');
77314
77315                 } else {
77316                     tooltip
77317                         .classed('in', false)
77318                         .call(uiToggle(false));
77319                 }
77320
77321                 curtain.cut(box, options.duration);
77322
77323                 return tooltip;
77324             };
77325
77326
77327             curtain.cut = function(datum, duration) {
77328                 darkness.datum(datum)
77329                     .interrupt();
77330
77331                 var selection;
77332                 if (duration === 0) {
77333                     selection = darkness;
77334                 } else {
77335                     selection = darkness
77336                         .transition()
77337                         .duration(duration || 600)
77338                         .ease(linear$1);
77339                 }
77340
77341                 selection
77342                     .attr('d', function(d) {
77343                         var containerWidth = containerNode.clientWidth;
77344                         var containerHeight = containerNode.clientHeight;
77345                         var string = 'M 0,0 L 0,' + containerHeight + ' L ' +
77346                             containerWidth + ',' + containerHeight + 'L' +
77347                             containerWidth + ',0 Z';
77348
77349                         if (!d) { return string; }
77350                         return string + 'M' +
77351                             d.left + ',' + d.top + 'L' +
77352                             d.left + ',' + (d.top + d.height) + 'L' +
77353                             (d.left + d.width) + ',' + (d.top + d.height) + 'L' +
77354                             (d.left + d.width) + ',' + (d.top) + 'Z';
77355
77356                     });
77357             };
77358
77359
77360             curtain.remove = function() {
77361                 surface.remove();
77362                 tooltip.remove();
77363                 select(window).on('resize.curtain', null);
77364             };
77365
77366
77367             // ClientRects are immutable, so copy them to an object,
77368             // in case we need to trim the height/width.
77369             function copyBox(src) {
77370                 return {
77371                     top: src.top,
77372                     right: src.right,
77373                     bottom: src.bottom,
77374                     left: src.left,
77375                     width: src.width,
77376                     height: src.height
77377                 };
77378             }
77379
77380
77381             return curtain;
77382         }
77383
77384         function uiIntroWelcome(context, reveal) {
77385             var dispatch$1 = dispatch('done');
77386
77387             var chapter = {
77388                 title: 'intro.welcome.title'
77389             };
77390
77391
77392             function welcome() {
77393                 context.map().centerZoom([-85.63591, 41.94285], 19);
77394                 reveal('.intro-nav-wrap .chapter-welcome',
77395                     helpString('intro.welcome.welcome'),
77396                     { buttonText: _t('intro.ok'), buttonCallback: practice }
77397                 );
77398             }
77399
77400             function practice() {
77401                 reveal('.intro-nav-wrap .chapter-welcome',
77402                     helpString('intro.welcome.practice'),
77403                     { buttonText: _t('intro.ok'), buttonCallback: words }
77404                 );
77405             }
77406
77407             function words() {
77408                 reveal('.intro-nav-wrap .chapter-welcome',
77409                     helpString('intro.welcome.words'),
77410                     { buttonText: _t('intro.ok'), buttonCallback: chapters }
77411                 );
77412             }
77413
77414
77415             function chapters() {
77416                 dispatch$1.call('done');
77417                 reveal('.intro-nav-wrap .chapter-navigation',
77418                     helpString('intro.welcome.chapters', { next: _t('intro.navigation.title') })
77419                 );
77420             }
77421
77422
77423             chapter.enter = function() {
77424                 welcome();
77425             };
77426
77427
77428             chapter.exit = function() {
77429                 context.container().select('.curtain-tooltip.intro-mouse')
77430                     .selectAll('.counter')
77431                     .remove();
77432             };
77433
77434
77435             chapter.restart = function() {
77436                 chapter.exit();
77437                 chapter.enter();
77438             };
77439
77440
77441             return utilRebind(chapter, dispatch$1, 'on');
77442         }
77443
77444         function uiIntroNavigation(context, reveal) {
77445             var dispatch$1 = dispatch('done');
77446             var timeouts = [];
77447             var hallId = 'n2061';
77448             var townHall = [-85.63591, 41.94285];
77449             var springStreetId = 'w397';
77450             var springStreetEndId = 'n1834';
77451             var springStreet = [-85.63582, 41.94255];
77452             var onewayField = _mainPresetIndex.field('oneway');
77453             var maxspeedField = _mainPresetIndex.field('maxspeed');
77454
77455
77456             var chapter = {
77457                 title: 'intro.navigation.title'
77458             };
77459
77460
77461             function timeout(f, t) {
77462                 timeouts.push(window.setTimeout(f, t));
77463             }
77464
77465
77466             function eventCancel() {
77467                 event.stopPropagation();
77468                 event.preventDefault();
77469             }
77470
77471
77472             function isTownHallSelected() {
77473                 var ids = context.selectedIDs();
77474                 return ids.length === 1 && ids[0] === hallId;
77475             }
77476
77477
77478             function dragMap() {
77479                 context.enter(modeBrowse(context));
77480                 context.history().reset('initial');
77481
77482                 var msec = transitionTime(townHall, context.map().center());
77483                 if (msec) { reveal(null, null, { duration: 0 }); }
77484                 context.map().centerZoomEase(townHall, 19, msec);
77485
77486                 timeout(function() {
77487                     var centerStart = context.map().center();
77488
77489                     var textId = context.lastPointerType() === 'mouse' ? 'drag' : 'drag_touch';
77490                     var dragString = helpString('intro.navigation.map_info') + '{br}' + helpString('intro.navigation.' + textId);
77491                     reveal('.surface', dragString);
77492                     context.map().on('drawn.intro', function() {
77493                         reveal('.surface', dragString, { duration: 0 });
77494                     });
77495
77496                     context.map().on('move.intro', function() {
77497                         var centerNow = context.map().center();
77498                         if (centerStart[0] !== centerNow[0] || centerStart[1] !== centerNow[1]) {
77499                             context.map().on('move.intro', null);
77500                             timeout(function() { continueTo(zoomMap); }, 3000);
77501                         }
77502                     });
77503
77504                 }, msec + 100);
77505
77506                 function continueTo(nextStep) {
77507                     context.map().on('move.intro drawn.intro', null);
77508                     nextStep();
77509                 }
77510             }
77511
77512
77513             function zoomMap() {
77514                 var zoomStart = context.map().zoom();
77515
77516                 var textId = context.lastPointerType() === 'mouse' ? 'zoom' : 'zoom_touch';
77517                 var zoomString = helpString('intro.navigation.' + textId);
77518
77519                 reveal('.surface', zoomString);
77520
77521                 context.map().on('drawn.intro', function() {
77522                     reveal('.surface', zoomString, { duration: 0 });
77523                 });
77524
77525                 context.map().on('move.intro', function() {
77526                     if (context.map().zoom() !== zoomStart) {
77527                         context.map().on('move.intro', null);
77528                         timeout(function() { continueTo(features); }, 3000);
77529                     }
77530                 });
77531
77532                 function continueTo(nextStep) {
77533                     context.map().on('move.intro drawn.intro', null);
77534                     nextStep();
77535                 }
77536             }
77537
77538
77539             function features() {
77540                 var onClick = function() { continueTo(pointsLinesAreas); };
77541
77542                 reveal('.surface', helpString('intro.navigation.features'),
77543                     { buttonText: _t('intro.ok'), buttonCallback: onClick }
77544                 );
77545
77546                 context.map().on('drawn.intro', function() {
77547                     reveal('.surface', helpString('intro.navigation.features'),
77548                         { duration: 0, buttonText: _t('intro.ok'), buttonCallback: onClick }
77549                     );
77550                 });
77551
77552                 function continueTo(nextStep) {
77553                     context.map().on('drawn.intro', null);
77554                     nextStep();
77555                 }
77556             }
77557
77558             function pointsLinesAreas() {
77559                 var onClick = function() { continueTo(nodesWays); };
77560
77561                 reveal('.surface', helpString('intro.navigation.points_lines_areas'),
77562                     { buttonText: _t('intro.ok'), buttonCallback: onClick }
77563                 );
77564
77565                 context.map().on('drawn.intro', function() {
77566                     reveal('.surface', helpString('intro.navigation.points_lines_areas'),
77567                         { duration: 0, buttonText: _t('intro.ok'), buttonCallback: onClick }
77568                     );
77569                 });
77570
77571                 function continueTo(nextStep) {
77572                     context.map().on('drawn.intro', null);
77573                     nextStep();
77574                 }
77575             }
77576
77577             function nodesWays() {
77578                 var onClick = function() { continueTo(clickTownHall); };
77579
77580                 reveal('.surface', helpString('intro.navigation.nodes_ways'),
77581                     { buttonText: _t('intro.ok'), buttonCallback: onClick }
77582                 );
77583
77584                 context.map().on('drawn.intro', function() {
77585                     reveal('.surface', helpString('intro.navigation.nodes_ways'),
77586                         { duration: 0, buttonText: _t('intro.ok'), buttonCallback: onClick }
77587                     );
77588                 });
77589
77590                 function continueTo(nextStep) {
77591                     context.map().on('drawn.intro', null);
77592                     nextStep();
77593                 }
77594             }
77595
77596             function clickTownHall() {
77597                 context.enter(modeBrowse(context));
77598                 context.history().reset('initial');
77599
77600                 var entity = context.hasEntity(hallId);
77601                 if (!entity) { return; }
77602                 reveal(null, null, { duration: 0 });
77603                 context.map().centerZoomEase(entity.loc, 19, 500);
77604
77605                 timeout(function() {
77606                     var entity = context.hasEntity(hallId);
77607                     if (!entity) { return; }
77608                     var box = pointBox(entity.loc, context);
77609                     var textId = context.lastPointerType() === 'mouse' ? 'click_townhall' : 'tap_townhall';
77610                     reveal(box, helpString('intro.navigation.' + textId));
77611
77612                     context.map().on('move.intro drawn.intro', function() {
77613                         var entity = context.hasEntity(hallId);
77614                         if (!entity) { return; }
77615                         var box = pointBox(entity.loc, context);
77616                         reveal(box, helpString('intro.navigation.' + textId), { duration: 0 });
77617                     });
77618
77619                     context.on('enter.intro', function() {
77620                         if (isTownHallSelected()) { continueTo(selectedTownHall); }
77621                     });
77622
77623                 }, 550);  // after centerZoomEase
77624
77625                 context.history().on('change.intro', function() {
77626                     if (!context.hasEntity(hallId)) {
77627                         continueTo(clickTownHall);
77628                     }
77629                 });
77630
77631                 function continueTo(nextStep) {
77632                     context.on('enter.intro', null);
77633                     context.map().on('move.intro drawn.intro', null);
77634                     context.history().on('change.intro', null);
77635                     nextStep();
77636                 }
77637             }
77638
77639
77640             function selectedTownHall() {
77641                 if (!isTownHallSelected()) { return clickTownHall(); }
77642
77643                 var entity = context.hasEntity(hallId);
77644                 if (!entity) { return clickTownHall(); }
77645
77646                 var box = pointBox(entity.loc, context);
77647                 var onClick = function() { continueTo(editorTownHall); };
77648
77649                 reveal(box, helpString('intro.navigation.selected_townhall'),
77650                     { buttonText: _t('intro.ok'), buttonCallback: onClick }
77651                 );
77652
77653                 context.map().on('move.intro drawn.intro', function() {
77654                     var entity = context.hasEntity(hallId);
77655                     if (!entity) { return; }
77656                     var box = pointBox(entity.loc, context);
77657                     reveal(box, helpString('intro.navigation.selected_townhall'),
77658                         { duration: 0, buttonText: _t('intro.ok'), buttonCallback: onClick }
77659                     );
77660                 });
77661
77662                 context.history().on('change.intro', function() {
77663                     if (!context.hasEntity(hallId)) {
77664                         continueTo(clickTownHall);
77665                     }
77666                 });
77667
77668                 function continueTo(nextStep) {
77669                     context.map().on('move.intro drawn.intro', null);
77670                     context.history().on('change.intro', null);
77671                     nextStep();
77672                 }
77673             }
77674
77675
77676             function editorTownHall() {
77677                 if (!isTownHallSelected()) { return clickTownHall(); }
77678
77679                 // disallow scrolling
77680                 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
77681
77682                 var onClick = function() { continueTo(presetTownHall); };
77683
77684                 reveal('.entity-editor-pane',
77685                     helpString('intro.navigation.editor_townhall'),
77686                     { buttonText: _t('intro.ok'), buttonCallback: onClick }
77687                 );
77688
77689                 context.on('exit.intro', function() {
77690                     continueTo(clickTownHall);
77691                 });
77692
77693                 context.history().on('change.intro', function() {
77694                     if (!context.hasEntity(hallId)) {
77695                         continueTo(clickTownHall);
77696                     }
77697                 });
77698
77699                 function continueTo(nextStep) {
77700                     context.on('exit.intro', null);
77701                     context.history().on('change.intro', null);
77702                     context.container().select('.inspector-wrap').on('wheel.intro', null);
77703                     nextStep();
77704                 }
77705             }
77706
77707
77708             function presetTownHall() {
77709                 if (!isTownHallSelected()) { return clickTownHall(); }
77710
77711                 // reset pane, in case user happened to change it..
77712                 context.container().select('.inspector-wrap .panewrap').style('right', '0%');
77713                 // disallow scrolling
77714                 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
77715
77716                 // preset match, in case the user happened to change it.
77717                 var entity = context.entity(context.selectedIDs()[0]);
77718                 var preset = _mainPresetIndex.match(entity, context.graph());
77719
77720                 var onClick = function() { continueTo(fieldsTownHall); };
77721
77722                 reveal('.entity-editor-pane .section-feature-type',
77723                     helpString('intro.navigation.preset_townhall', { preset: preset.name() }),
77724                     { buttonText: _t('intro.ok'), buttonCallback: onClick }
77725                 );
77726
77727                 context.on('exit.intro', function() {
77728                     continueTo(clickTownHall);
77729                 });
77730
77731                 context.history().on('change.intro', function() {
77732                     if (!context.hasEntity(hallId)) {
77733                         continueTo(clickTownHall);
77734                     }
77735                 });
77736
77737                 function continueTo(nextStep) {
77738                     context.on('exit.intro', null);
77739                     context.history().on('change.intro', null);
77740                     context.container().select('.inspector-wrap').on('wheel.intro', null);
77741                     nextStep();
77742                 }
77743             }
77744
77745
77746             function fieldsTownHall() {
77747                 if (!isTownHallSelected()) { return clickTownHall(); }
77748
77749                 // reset pane, in case user happened to change it..
77750                 context.container().select('.inspector-wrap .panewrap').style('right', '0%');
77751                 // disallow scrolling
77752                 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
77753
77754                 var onClick = function() { continueTo(closeTownHall); };
77755
77756                 reveal('.entity-editor-pane .section-preset-fields',
77757                     helpString('intro.navigation.fields_townhall'),
77758                     { buttonText: _t('intro.ok'), buttonCallback: onClick }
77759                 );
77760
77761                 context.on('exit.intro', function() {
77762                     continueTo(clickTownHall);
77763                 });
77764
77765                 context.history().on('change.intro', function() {
77766                     if (!context.hasEntity(hallId)) {
77767                         continueTo(clickTownHall);
77768                     }
77769                 });
77770
77771                 function continueTo(nextStep) {
77772                     context.on('exit.intro', null);
77773                     context.history().on('change.intro', null);
77774                     context.container().select('.inspector-wrap').on('wheel.intro', null);
77775                     nextStep();
77776                 }
77777             }
77778
77779
77780             function closeTownHall() {
77781                 if (!isTownHallSelected()) { return clickTownHall(); }
77782
77783                 var selector = '.entity-editor-pane button.close svg use';
77784                 var href = select(selector).attr('href') || '#iD-icon-close';
77785
77786                 reveal('.entity-editor-pane',
77787                     helpString('intro.navigation.close_townhall', { button: icon(href, 'pre-text') })
77788                 );
77789
77790                 context.on('exit.intro', function() {
77791                     continueTo(searchStreet);
77792                 });
77793
77794                 context.history().on('change.intro', function() {
77795                     // update the close icon in the tooltip if the user edits something.
77796                     var selector = '.entity-editor-pane button.close svg use';
77797                     var href = select(selector).attr('href') || '#iD-icon-close';
77798
77799                     reveal('.entity-editor-pane',
77800                         helpString('intro.navigation.close_townhall', { button: icon(href, 'pre-text') }),
77801                         { duration: 0 }
77802                     );
77803                 });
77804
77805                 function continueTo(nextStep) {
77806                     context.on('exit.intro', null);
77807                     context.history().on('change.intro', null);
77808                     nextStep();
77809                 }
77810             }
77811
77812
77813             function searchStreet() {
77814                 context.enter(modeBrowse(context));
77815                 context.history().reset('initial');  // ensure spring street exists
77816
77817                 var msec = transitionTime(springStreet, context.map().center());
77818                 if (msec) { reveal(null, null, { duration: 0 }); }
77819                 context.map().centerZoomEase(springStreet, 19, msec);  // ..and user can see it
77820
77821                 timeout(function() {
77822                     reveal('.search-header input',
77823                         helpString('intro.navigation.search_street', { name: _t('intro.graph.name.spring-street') })
77824                     );
77825
77826                     context.container().select('.search-header input')
77827                         .on('keyup.intro', checkSearchResult);
77828                 }, msec + 100);
77829             }
77830
77831
77832             function checkSearchResult() {
77833                 var first = context.container().select('.feature-list-item:nth-child(0n+2)');  // skip "No Results" item
77834                 var firstName = first.select('.entity-name');
77835                 var name = _t('intro.graph.name.spring-street');
77836
77837                 if (!firstName.empty() && firstName.text() === name) {
77838                     reveal(first.node(),
77839                         helpString('intro.navigation.choose_street', { name: name }),
77840                         { duration: 300 }
77841                     );
77842
77843                     context.on('exit.intro', function() {
77844                         continueTo(selectedStreet);
77845                     });
77846
77847                     context.container().select('.search-header input')
77848                         .on('keydown.intro', eventCancel, true)
77849                         .on('keyup.intro', null);
77850                 }
77851
77852                 function continueTo(nextStep) {
77853                     context.on('exit.intro', null);
77854                     context.container().select('.search-header input')
77855                         .on('keydown.intro', null)
77856                         .on('keyup.intro', null);
77857                     nextStep();
77858                 }
77859             }
77860
77861
77862             function selectedStreet() {
77863                 if (!context.hasEntity(springStreetEndId) || !context.hasEntity(springStreetId)) {
77864                     return searchStreet();
77865                 }
77866
77867                 var onClick = function() { continueTo(editorStreet); };
77868                 var entity = context.entity(springStreetEndId);
77869                 var box = pointBox(entity.loc, context);
77870                 box.height = 500;
77871
77872                 reveal(box,
77873                     helpString('intro.navigation.selected_street', { name: _t('intro.graph.name.spring-street') }),
77874                     { duration: 600, buttonText: _t('intro.ok'), buttonCallback: onClick }
77875                 );
77876
77877                 timeout(function() {
77878                     context.map().on('move.intro drawn.intro', function() {
77879                         var entity = context.hasEntity(springStreetEndId);
77880                         if (!entity) { return; }
77881                         var box = pointBox(entity.loc, context);
77882                         box.height = 500;
77883                         reveal(box,
77884                             helpString('intro.navigation.selected_street', { name: _t('intro.graph.name.spring-street') }),
77885                             { duration: 0, buttonText: _t('intro.ok'), buttonCallback: onClick }
77886                         );
77887                     });
77888                 }, 600);  // after reveal.
77889
77890                 context.on('enter.intro', function(mode) {
77891                     if (!context.hasEntity(springStreetId)) {
77892                         return continueTo(searchStreet);
77893                     }
77894                     var ids = context.selectedIDs();
77895                     if (mode.id !== 'select' || !ids.length || ids[0] !== springStreetId) {
77896                         // keep Spring Street selected..
77897                         context.enter(modeSelect(context, [springStreetId]));
77898                     }
77899                 });
77900
77901                 context.history().on('change.intro', function() {
77902                     if (!context.hasEntity(springStreetEndId) || !context.hasEntity(springStreetId)) {
77903                         timeout(function() {
77904                             continueTo(searchStreet);
77905                         }, 300);  // after any transition (e.g. if user deleted intersection)
77906                     }
77907                 });
77908
77909                 function continueTo(nextStep) {
77910                     context.map().on('move.intro drawn.intro', null);
77911                     context.on('enter.intro', null);
77912                     context.history().on('change.intro', null);
77913                     nextStep();
77914                 }
77915             }
77916
77917
77918             function editorStreet() {
77919                 var selector = '.entity-editor-pane button.close svg use';
77920                 var href = select(selector).attr('href') || '#iD-icon-close';
77921
77922                 reveal('.entity-editor-pane', helpString('intro.navigation.street_different_fields') + '{br}' +
77923                     helpString('intro.navigation.editor_street', {
77924                         button: icon(href, 'pre-text'),
77925                         field1: onewayField.label(),
77926                         field2: maxspeedField.label()
77927                     }));
77928
77929                 context.on('exit.intro', function() {
77930                     continueTo(play);
77931                 });
77932
77933                 context.history().on('change.intro', function() {
77934                     // update the close icon in the tooltip if the user edits something.
77935                     var selector = '.entity-editor-pane button.close svg use';
77936                     var href = select(selector).attr('href') || '#iD-icon-close';
77937
77938                     reveal('.entity-editor-pane', helpString('intro.navigation.street_different_fields') + '{br}' +
77939                         helpString('intro.navigation.editor_street', {
77940                             button: icon(href, 'pre-text'),
77941                             field1: onewayField.label(),
77942                             field2: maxspeedField.label()
77943                         }), { duration: 0 }
77944                     );
77945                 });
77946
77947                 function continueTo(nextStep) {
77948                     context.on('exit.intro', null);
77949                     context.history().on('change.intro', null);
77950                     nextStep();
77951                 }
77952             }
77953
77954
77955             function play() {
77956                 dispatch$1.call('done');
77957                 reveal('.ideditor',
77958                     helpString('intro.navigation.play', { next: _t('intro.points.title') }), {
77959                         tooltipBox: '.intro-nav-wrap .chapter-point',
77960                         buttonText: _t('intro.ok'),
77961                         buttonCallback: function() { reveal('.ideditor'); }
77962                     }
77963                 );
77964             }
77965
77966
77967             chapter.enter = function() {
77968                 dragMap();
77969             };
77970
77971
77972             chapter.exit = function() {
77973                 timeouts.forEach(window.clearTimeout);
77974                 context.on('enter.intro exit.intro', null);
77975                 context.map().on('move.intro drawn.intro', null);
77976                 context.history().on('change.intro', null);
77977                 context.container().select('.inspector-wrap').on('wheel.intro', null);
77978                 context.container().select('.search-header input').on('keydown.intro keyup.intro', null);
77979             };
77980
77981
77982             chapter.restart = function() {
77983                 chapter.exit();
77984                 chapter.enter();
77985             };
77986
77987
77988             return utilRebind(chapter, dispatch$1, 'on');
77989         }
77990
77991         function uiIntroPoint(context, reveal) {
77992             var dispatch$1 = dispatch('done');
77993             var timeouts = [];
77994             var intersection = [-85.63279, 41.94394];
77995             var building = [-85.632422, 41.944045];
77996             var cafePreset = _mainPresetIndex.item('amenity/cafe');
77997             var _pointID = null;
77998
77999
78000             var chapter = {
78001                 title: 'intro.points.title'
78002             };
78003
78004
78005             function timeout(f, t) {
78006                 timeouts.push(window.setTimeout(f, t));
78007             }
78008
78009
78010             function eventCancel() {
78011                 event.stopPropagation();
78012                 event.preventDefault();
78013             }
78014
78015
78016             function addPoint() {
78017                 context.enter(modeBrowse(context));
78018                 context.history().reset('initial');
78019
78020                 var msec = transitionTime(intersection, context.map().center());
78021                 if (msec) { reveal(null, null, { duration: 0 }); }
78022                 context.map().centerZoomEase(intersection, 19, msec);
78023
78024                 timeout(function() {
78025                     var tooltip = reveal('button.add-point',
78026                         helpString('intro.points.points_info') + '{br}' + helpString('intro.points.add_point'));
78027
78028                     _pointID = null;
78029
78030                     tooltip.selectAll('.popover-inner')
78031                         .insert('svg', 'span')
78032                         .attr('class', 'tooltip-illustration')
78033                         .append('use')
78034                         .attr('xlink:href', '#iD-graphic-points');
78035
78036                     context.on('enter.intro', function(mode) {
78037                         if (mode.id !== 'add-point') { return; }
78038                         continueTo(placePoint);
78039                     });
78040                 }, msec + 100);
78041
78042                 function continueTo(nextStep) {
78043                     context.on('enter.intro', null);
78044                     nextStep();
78045                 }
78046             }
78047
78048
78049             function placePoint() {
78050                 if (context.mode().id !== 'add-point') {
78051                     return chapter.restart();
78052                 }
78053
78054                 var pointBox = pad(building, 150, context);
78055                 var textId = context.lastPointerType() === 'mouse' ? 'place_point' : 'place_point_touch';
78056                 reveal(pointBox, helpString('intro.points.' + textId));
78057
78058                 context.map().on('move.intro drawn.intro', function() {
78059                     pointBox = pad(building, 150, context);
78060                     reveal(pointBox, helpString('intro.points.' + textId), { duration: 0 });
78061                 });
78062
78063                 context.on('enter.intro', function(mode) {
78064                     if (mode.id !== 'select') { return chapter.restart(); }
78065                     _pointID = context.mode().selectedIDs()[0];
78066                     continueTo(searchPreset);
78067                 });
78068
78069                 function continueTo(nextStep) {
78070                     context.map().on('move.intro drawn.intro', null);
78071                     context.on('enter.intro', null);
78072                     nextStep();
78073                 }
78074             }
78075
78076
78077             function searchPreset() {
78078                 if (context.mode().id !== 'select' || !_pointID || !context.hasEntity(_pointID)) {
78079                     return addPoint();
78080                 }
78081
78082                 // disallow scrolling
78083                 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
78084
78085                 context.container().select('.preset-search-input')
78086                     .on('keydown.intro', null)
78087                     .on('keyup.intro', checkPresetSearch);
78088
78089                 reveal('.preset-search-input',
78090                     helpString('intro.points.search_cafe', { preset: cafePreset.name() })
78091                 );
78092
78093                 context.on('enter.intro', function(mode) {
78094                     if (!_pointID || !context.hasEntity(_pointID)) {
78095                         return continueTo(addPoint);
78096                     }
78097
78098                     var ids = context.selectedIDs();
78099                     if (mode.id !== 'select' || !ids.length || ids[0] !== _pointID) {
78100                         // keep the user's point selected..
78101                         context.enter(modeSelect(context, [_pointID]));
78102
78103                         // disallow scrolling
78104                         context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
78105
78106                         context.container().select('.preset-search-input')
78107                             .on('keydown.intro', null)
78108                             .on('keyup.intro', checkPresetSearch);
78109
78110                         reveal('.preset-search-input',
78111                             helpString('intro.points.search_cafe', { preset: cafePreset.name() })
78112                         );
78113
78114                         context.history().on('change.intro', null);
78115                     }
78116                 });
78117
78118
78119                 function checkPresetSearch() {
78120                     var first = context.container().select('.preset-list-item:first-child');
78121
78122                     if (first.classed('preset-amenity-cafe')) {
78123                         context.container().select('.preset-search-input')
78124                             .on('keydown.intro', eventCancel, true)
78125                             .on('keyup.intro', null);
78126
78127                         reveal(first.select('.preset-list-button').node(),
78128                             helpString('intro.points.choose_cafe', { preset: cafePreset.name() }),
78129                             { duration: 300 }
78130                         );
78131
78132                         context.history().on('change.intro', function() {
78133                             continueTo(aboutFeatureEditor);
78134                         });
78135                     }
78136                 }
78137
78138                 function continueTo(nextStep) {
78139                     context.on('enter.intro', null);
78140                     context.history().on('change.intro', null);
78141                     context.container().select('.inspector-wrap').on('wheel.intro', null);
78142                     context.container().select('.preset-search-input').on('keydown.intro keyup.intro', null);
78143                     nextStep();
78144                 }
78145             }
78146
78147
78148             function aboutFeatureEditor() {
78149                 if (context.mode().id !== 'select' || !_pointID || !context.hasEntity(_pointID)) {
78150                     return addPoint();
78151                 }
78152
78153                 timeout(function() {
78154                     reveal('.entity-editor-pane', helpString('intro.points.feature_editor'), {
78155                         tooltipClass: 'intro-points-describe',
78156                         buttonText: _t('intro.ok'),
78157                         buttonCallback: function() { continueTo(addName); }
78158                     });
78159                 }, 400);
78160
78161                 context.on('exit.intro', function() {
78162                     // if user leaves select mode here, just continue with the tutorial.
78163                     continueTo(reselectPoint);
78164                 });
78165
78166                 function continueTo(nextStep) {
78167                     context.on('exit.intro', null);
78168                     nextStep();
78169                 }
78170             }
78171
78172
78173             function addName() {
78174                 if (context.mode().id !== 'select' || !_pointID || !context.hasEntity(_pointID)) {
78175                     return addPoint();
78176                 }
78177
78178                 // reset pane, in case user happened to change it..
78179                 context.container().select('.inspector-wrap .panewrap').style('right', '0%');
78180
78181                 var addNameString = helpString('intro.points.fields_info') + '{br}' + helpString('intro.points.add_name');
78182
78183                 timeout(function() {
78184                     // It's possible for the user to add a name in a previous step..
78185                     // If so, don't tell them to add the name in this step.
78186                     // Give them an OK button instead.
78187                     var entity = context.entity(_pointID);
78188                     if (entity.tags.name) {
78189                         var tooltip = reveal('.entity-editor-pane', addNameString, {
78190                             tooltipClass: 'intro-points-describe',
78191                             buttonText: _t('intro.ok'),
78192                             buttonCallback: function() { continueTo(addCloseEditor); }
78193                         });
78194                         tooltip.select('.instruction').style('display', 'none');
78195
78196                     } else {
78197                         reveal('.entity-editor-pane', addNameString,
78198                             { tooltipClass: 'intro-points-describe' }
78199                         );
78200                     }
78201                 }, 400);
78202
78203                 context.history().on('change.intro', function() {
78204                     continueTo(addCloseEditor);
78205                 });
78206
78207                 context.on('exit.intro', function() {
78208                     // if user leaves select mode here, just continue with the tutorial.
78209                     continueTo(reselectPoint);
78210                 });
78211
78212                 function continueTo(nextStep) {
78213                     context.on('exit.intro', null);
78214                     context.history().on('change.intro', null);
78215                     nextStep();
78216                 }
78217             }
78218
78219
78220             function addCloseEditor() {
78221                 // reset pane, in case user happened to change it..
78222                 context.container().select('.inspector-wrap .panewrap').style('right', '0%');
78223
78224                 var selector = '.entity-editor-pane button.close svg use';
78225                 var href = select(selector).attr('href') || '#iD-icon-close';
78226
78227                 context.on('exit.intro', function() {
78228                     continueTo(reselectPoint);
78229                 });
78230
78231                 reveal('.entity-editor-pane',
78232                     helpString('intro.points.add_close', { button: icon(href, 'pre-text') })
78233                 );
78234
78235                 function continueTo(nextStep) {
78236                     context.on('exit.intro', null);
78237                     nextStep();
78238                 }
78239             }
78240
78241
78242             function reselectPoint() {
78243                 if (!_pointID) { return chapter.restart(); }
78244                 var entity = context.hasEntity(_pointID);
78245                 if (!entity) { return chapter.restart(); }
78246
78247                 // make sure it's still a cafe, in case user somehow changed it..
78248                 var oldPreset = _mainPresetIndex.match(entity, context.graph());
78249                 context.replace(actionChangePreset(_pointID, oldPreset, cafePreset));
78250
78251                 context.enter(modeBrowse(context));
78252
78253                 var msec = transitionTime(entity.loc, context.map().center());
78254                 if (msec) { reveal(null, null, { duration: 0 }); }
78255                 context.map().centerEase(entity.loc, msec);
78256
78257                 timeout(function() {
78258                     var box = pointBox(entity.loc, context);
78259                     reveal(box, helpString('intro.points.reselect'), { duration: 600 });
78260
78261                     timeout(function() {
78262                         context.map().on('move.intro drawn.intro', function() {
78263                             var entity = context.hasEntity(_pointID);
78264                             if (!entity) { return chapter.restart(); }
78265                             var box = pointBox(entity.loc, context);
78266                             reveal(box, helpString('intro.points.reselect'), { duration: 0 });
78267                         });
78268                     }, 600); // after reveal..
78269
78270                     context.on('enter.intro', function(mode) {
78271                         if (mode.id !== 'select') { return; }
78272                         continueTo(updatePoint);
78273                     });
78274
78275                 }, msec + 100);
78276
78277                 function continueTo(nextStep) {
78278                     context.map().on('move.intro drawn.intro', null);
78279                     context.on('enter.intro', null);
78280                     nextStep();
78281                 }
78282             }
78283
78284
78285             function updatePoint() {
78286                 if (context.mode().id !== 'select' || !_pointID || !context.hasEntity(_pointID)) {
78287                     return continueTo(reselectPoint);
78288                 }
78289
78290                 // reset pane, in case user happened to untag the point..
78291                 context.container().select('.inspector-wrap .panewrap').style('right', '0%');
78292
78293                 context.on('exit.intro', function() {
78294                     continueTo(reselectPoint);
78295                 });
78296
78297                 context.history().on('change.intro', function() {
78298                     continueTo(updateCloseEditor);
78299                 });
78300
78301                 timeout(function() {
78302                     reveal('.entity-editor-pane', helpString('intro.points.update'),
78303                         { tooltipClass: 'intro-points-describe' }
78304                     );
78305                 }, 400);
78306
78307                 function continueTo(nextStep) {
78308                     context.on('exit.intro', null);
78309                     context.history().on('change.intro', null);
78310                     nextStep();
78311                 }
78312             }
78313
78314
78315             function updateCloseEditor() {
78316                 if (context.mode().id !== 'select' || !_pointID || !context.hasEntity(_pointID)) {
78317                     return continueTo(reselectPoint);
78318                 }
78319
78320                 // reset pane, in case user happened to change it..
78321                 context.container().select('.inspector-wrap .panewrap').style('right', '0%');
78322
78323                 context.on('exit.intro', function() {
78324                     continueTo(rightClickPoint);
78325                 });
78326
78327                 timeout(function() {
78328                     reveal('.entity-editor-pane',
78329                         helpString('intro.points.update_close', { button: icon('#iD-icon-close', 'pre-text') })
78330                     );
78331                 }, 500);
78332
78333                 function continueTo(nextStep) {
78334                     context.on('exit.intro', null);
78335                     nextStep();
78336                 }
78337             }
78338
78339
78340             function rightClickPoint() {
78341                 if (!_pointID) { return chapter.restart(); }
78342                 var entity = context.hasEntity(_pointID);
78343                 if (!entity) { return chapter.restart(); }
78344
78345                 context.enter(modeBrowse(context));
78346
78347                 var box = pointBox(entity.loc, context);
78348                 var textId = context.lastPointerType() === 'mouse' ? 'rightclick' : 'edit_menu_touch';
78349                 reveal(box, helpString('intro.points.' + textId), { duration: 600 });
78350
78351                 timeout(function() {
78352                     context.map().on('move.intro', function() {
78353                         var entity = context.hasEntity(_pointID);
78354                         if (!entity) { return chapter.restart(); }
78355                         var box = pointBox(entity.loc, context);
78356                         reveal(box, helpString('intro.points.' + textId), { duration: 0 });
78357                     });
78358                 }, 600); // after reveal
78359
78360                 context.on('enter.intro', function(mode) {
78361                     if (mode.id !== 'select') { return; }
78362                     var ids = context.selectedIDs();
78363                     if (ids.length !== 1 || ids[0] !== _pointID) { return; }
78364
78365                     timeout(function() {
78366                         var node = selectMenuItem(context, 'delete').node();
78367                         if (!node) { return; }
78368                         continueTo(enterDelete);
78369                     }, 50);  // after menu visible
78370                 });
78371
78372                 function continueTo(nextStep) {
78373                     context.on('enter.intro', null);
78374                     context.map().on('move.intro', null);
78375                     nextStep();
78376                 }
78377             }
78378
78379
78380             function enterDelete() {
78381                 if (!_pointID) { return chapter.restart(); }
78382                 var entity = context.hasEntity(_pointID);
78383                 if (!entity) { return chapter.restart(); }
78384
78385                 var node = selectMenuItem(context, 'delete').node();
78386                 if (!node) { return continueTo(rightClickPoint); }
78387
78388                 reveal('.edit-menu',
78389                     helpString('intro.points.delete'),
78390                     { padding: 50 }
78391                 );
78392
78393                 timeout(function() {
78394                     context.map().on('move.intro', function() {
78395                         reveal('.edit-menu',
78396                             helpString('intro.points.delete'),
78397                             { duration: 0,  padding: 50 }
78398                         );
78399                     });
78400                 }, 300); // after menu visible
78401
78402                 context.on('exit.intro', function() {
78403                     if (!_pointID) { return chapter.restart(); }
78404                     var entity = context.hasEntity(_pointID);
78405                     if (entity) { return continueTo(rightClickPoint); }  // point still exists
78406                 });
78407
78408                 context.history().on('change.intro', function(changed) {
78409                     if (changed.deleted().length) {
78410                         continueTo(undo);
78411                     }
78412                 });
78413
78414                 function continueTo(nextStep) {
78415                     context.map().on('move.intro', null);
78416                     context.history().on('change.intro', null);
78417                     context.on('exit.intro', null);
78418                     nextStep();
78419                 }
78420             }
78421
78422
78423             function undo() {
78424                 context.history().on('change.intro', function() {
78425                     continueTo(play);
78426                 });
78427
78428                 reveal('.top-toolbar button.undo-button',
78429                     helpString('intro.points.undo')
78430                 );
78431
78432                 function continueTo(nextStep) {
78433                     context.history().on('change.intro', null);
78434                     nextStep();
78435                 }
78436             }
78437
78438
78439             function play() {
78440                 dispatch$1.call('done');
78441                 reveal('.ideditor',
78442                     helpString('intro.points.play', { next: _t('intro.areas.title') }), {
78443                         tooltipBox: '.intro-nav-wrap .chapter-area',
78444                         buttonText: _t('intro.ok'),
78445                         buttonCallback: function() { reveal('.ideditor'); }
78446                     }
78447                 );
78448             }
78449
78450
78451             chapter.enter = function() {
78452                 addPoint();
78453             };
78454
78455
78456             chapter.exit = function() {
78457                 timeouts.forEach(window.clearTimeout);
78458                 context.on('enter.intro exit.intro', null);
78459                 context.map().on('move.intro drawn.intro', null);
78460                 context.history().on('change.intro', null);
78461                 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
78462                 context.container().select('.preset-search-input').on('keydown.intro keyup.intro', null);
78463             };
78464
78465
78466             chapter.restart = function() {
78467                 chapter.exit();
78468                 chapter.enter();
78469             };
78470
78471
78472             return utilRebind(chapter, dispatch$1, 'on');
78473         }
78474
78475         function uiIntroArea(context, reveal) {
78476             var dispatch$1 = dispatch('done');
78477             var playground = [-85.63552, 41.94159];
78478             var playgroundPreset = _mainPresetIndex.item('leisure/playground');
78479             var nameField = _mainPresetIndex.field('name');
78480             var descriptionField = _mainPresetIndex.field('description');
78481             var timeouts = [];
78482             var _areaID;
78483
78484
78485             var chapter = {
78486                 title: 'intro.areas.title'
78487             };
78488
78489
78490             function timeout(f, t) {
78491                 timeouts.push(window.setTimeout(f, t));
78492             }
78493
78494
78495             function eventCancel() {
78496                 event.stopPropagation();
78497                 event.preventDefault();
78498             }
78499
78500
78501             function revealPlayground(center, text, options) {
78502                 var padding = 180 * Math.pow(2, context.map().zoom() - 19.5);
78503                 var box = pad(center, padding, context);
78504                 reveal(box, text, options);
78505             }
78506
78507
78508             function addArea() {
78509                 context.enter(modeBrowse(context));
78510                 context.history().reset('initial');
78511                 _areaID = null;
78512
78513                 var msec = transitionTime(playground, context.map().center());
78514                 if (msec) { reveal(null, null, { duration: 0 }); }
78515                 context.map().centerZoomEase(playground, 19, msec);
78516
78517                 timeout(function() {
78518                     var tooltip = reveal('button.add-area',
78519                         helpString('intro.areas.add_playground'));
78520
78521                     tooltip.selectAll('.popover-inner')
78522                         .insert('svg', 'span')
78523                         .attr('class', 'tooltip-illustration')
78524                         .append('use')
78525                         .attr('xlink:href', '#iD-graphic-areas');
78526
78527                     context.on('enter.intro', function(mode) {
78528                         if (mode.id !== 'add-area') { return; }
78529                         continueTo(startPlayground);
78530                     });
78531                 }, msec + 100);
78532
78533                 function continueTo(nextStep) {
78534                     context.on('enter.intro', null);
78535                     nextStep();
78536                 }
78537             }
78538
78539
78540             function startPlayground() {
78541                 if (context.mode().id !== 'add-area') {
78542                     return chapter.restart();
78543                 }
78544
78545                 _areaID = null;
78546                 context.map().zoomEase(19.5, 500);
78547
78548                 timeout(function() {
78549                     var textId = context.lastPointerType() === 'mouse' ? 'starting_node_click' : 'starting_node_tap';
78550                     var startDrawString = helpString('intro.areas.start_playground') + helpString('intro.areas.' + textId);
78551                     revealPlayground(playground,
78552                         startDrawString, { duration: 250 }
78553                     );
78554
78555                     timeout(function() {
78556                         context.map().on('move.intro drawn.intro', function() {
78557                             revealPlayground(playground,
78558                                 startDrawString, { duration: 0 }
78559                             );
78560                         });
78561                         context.on('enter.intro', function(mode) {
78562                             if (mode.id !== 'draw-area') { return chapter.restart(); }
78563                             continueTo(continuePlayground);
78564                         });
78565                     }, 250);  // after reveal
78566
78567                 }, 550);  // after easing
78568
78569                 function continueTo(nextStep) {
78570                     context.map().on('move.intro drawn.intro', null);
78571                     context.on('enter.intro', null);
78572                     nextStep();
78573                 }
78574             }
78575
78576
78577             function continuePlayground() {
78578                 if (context.mode().id !== 'draw-area') {
78579                     return chapter.restart();
78580                 }
78581
78582                 _areaID = null;
78583                 revealPlayground(playground,
78584                     helpString('intro.areas.continue_playground'),
78585                     { duration: 250 }
78586                 );
78587
78588                 timeout(function() {
78589                     context.map().on('move.intro drawn.intro', function() {
78590                         revealPlayground(playground,
78591                             helpString('intro.areas.continue_playground'),
78592                             { duration: 0 }
78593                         );
78594                     });
78595                 }, 250);  // after reveal
78596
78597                 context.on('enter.intro', function(mode) {
78598                     if (mode.id === 'draw-area') {
78599                         var entity = context.hasEntity(context.selectedIDs()[0]);
78600                         if (entity && entity.nodes.length >= 6) {
78601                             return continueTo(finishPlayground);
78602                         } else {
78603                             return;
78604                         }
78605                     } else if (mode.id === 'select') {
78606                         _areaID = context.selectedIDs()[0];
78607                         return continueTo(searchPresets);
78608                     } else {
78609                         return chapter.restart();
78610                     }
78611                 });
78612
78613                 function continueTo(nextStep) {
78614                     context.map().on('move.intro drawn.intro', null);
78615                     context.on('enter.intro', null);
78616                     nextStep();
78617                 }
78618             }
78619
78620
78621             function finishPlayground() {
78622                 if (context.mode().id !== 'draw-area') {
78623                     return chapter.restart();
78624                 }
78625
78626                 _areaID = null;
78627
78628                 var finishString = helpString('intro.areas.finish_area_' + (context.lastPointerType() === 'mouse' ? 'click' : 'tap')) +
78629                     helpString('intro.areas.finish_playground');
78630                 revealPlayground(playground,
78631                     finishString, { duration: 250 }
78632                 );
78633
78634                 timeout(function() {
78635                     context.map().on('move.intro drawn.intro', function() {
78636                         revealPlayground(playground,
78637                             finishString, { duration: 0 }
78638                         );
78639                     });
78640                 }, 250);  // after reveal
78641
78642                 context.on('enter.intro', function(mode) {
78643                     if (mode.id === 'draw-area') {
78644                         return;
78645                     } else if (mode.id === 'select') {
78646                         _areaID = context.selectedIDs()[0];
78647                         return continueTo(searchPresets);
78648                     } else {
78649                         return chapter.restart();
78650                     }
78651                 });
78652
78653                 function continueTo(nextStep) {
78654                     context.map().on('move.intro drawn.intro', null);
78655                     context.on('enter.intro', null);
78656                     nextStep();
78657                 }
78658             }
78659
78660
78661             function searchPresets() {
78662                 if (!_areaID || !context.hasEntity(_areaID)) {
78663                     return addArea();
78664                 }
78665                 var ids = context.selectedIDs();
78666                 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _areaID) {
78667                     context.enter(modeSelect(context, [_areaID]));
78668                 }
78669
78670                 // disallow scrolling
78671                 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
78672
78673                 timeout(function() {
78674                     // reset pane, in case user somehow happened to change it..
78675                     context.container().select('.inspector-wrap .panewrap').style('right', '-100%');
78676
78677                     context.container().select('.preset-search-input')
78678                         .on('keydown.intro', null)
78679                         .on('keyup.intro', checkPresetSearch);
78680
78681                     reveal('.preset-search-input',
78682                         helpString('intro.areas.search_playground', { preset: playgroundPreset.name() })
78683                     );
78684                 }, 400);  // after preset list pane visible..
78685
78686                 context.on('enter.intro', function(mode) {
78687                     if (!_areaID || !context.hasEntity(_areaID)) {
78688                         return continueTo(addArea);
78689                     }
78690
78691                     var ids = context.selectedIDs();
78692                     if (mode.id !== 'select' || !ids.length || ids[0] !== _areaID) {
78693                         // keep the user's area selected..
78694                         context.enter(modeSelect(context, [_areaID]));
78695
78696                         // reset pane, in case user somehow happened to change it..
78697                         context.container().select('.inspector-wrap .panewrap').style('right', '-100%');
78698                         // disallow scrolling
78699                         context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
78700
78701                         context.container().select('.preset-search-input')
78702                             .on('keydown.intro', null)
78703                             .on('keyup.intro', checkPresetSearch);
78704
78705                         reveal('.preset-search-input',
78706                             helpString('intro.areas.search_playground', { preset: playgroundPreset.name() })
78707                         );
78708
78709                         context.history().on('change.intro', null);
78710                     }
78711                 });
78712
78713                 function checkPresetSearch() {
78714                     var first = context.container().select('.preset-list-item:first-child');
78715
78716                     if (first.classed('preset-leisure-playground')) {
78717                         reveal(first.select('.preset-list-button').node(),
78718                             helpString('intro.areas.choose_playground', { preset: playgroundPreset.name() }),
78719                             { duration: 300 }
78720                         );
78721
78722                         context.container().select('.preset-search-input')
78723                             .on('keydown.intro', eventCancel, true)
78724                             .on('keyup.intro', null);
78725
78726                         context.history().on('change.intro', function() {
78727                             continueTo(clickAddField);
78728                         });
78729                     }
78730                 }
78731
78732                 function continueTo(nextStep) {
78733                     context.container().select('.inspector-wrap').on('wheel.intro', null);
78734                     context.on('enter.intro', null);
78735                     context.history().on('change.intro', null);
78736                     context.container().select('.preset-search-input').on('keydown.intro keyup.intro', null);
78737                     nextStep();
78738                 }
78739             }
78740
78741
78742             function clickAddField() {
78743                 if (!_areaID || !context.hasEntity(_areaID)) {
78744                     return addArea();
78745                 }
78746                 var ids = context.selectedIDs();
78747                 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _areaID) {
78748                     return searchPresets();
78749                 }
78750
78751                 if (!context.container().select('.form-field-description').empty()) {
78752                     return continueTo(describePlayground);
78753                 }
78754
78755                 // disallow scrolling
78756                 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
78757
78758                 timeout(function() {
78759                     // reset pane, in case user somehow happened to change it..
78760                     context.container().select('.inspector-wrap .panewrap').style('right', '0%');
78761
78762                     // It's possible for the user to add a description in a previous step..
78763                     // If they did this already, just continue to next step.
78764                     var entity = context.entity(_areaID);
78765                     if (entity.tags.description) {
78766                         return continueTo(play);
78767                     }
78768
78769                     // scroll "Add field" into view
78770                     var box = context.container().select('.more-fields').node().getBoundingClientRect();
78771                     if (box.top > 300) {
78772                         var pane = context.container().select('.entity-editor-pane .inspector-body');
78773                         var start = pane.node().scrollTop;
78774                         var end = start + (box.top - 300);
78775
78776                         pane
78777                             .transition()
78778                             .duration(250)
78779                             .tween('scroll.inspector', function() {
78780                                 var node = this;
78781                                 var i = d3_interpolateNumber(start, end);
78782                                 return function(t) {
78783                                     node.scrollTop = i(t);
78784                                 };
78785                             });
78786                     }
78787
78788                     timeout(function() {
78789                         reveal('.more-fields .combobox-input',
78790                             helpString('intro.areas.add_field', {
78791                                 name: nameField.label(),
78792                                 description: descriptionField.label()
78793                             }),
78794                             { duration: 300 }
78795                         );
78796
78797                         context.container().select('.more-fields .combobox-input')
78798                             .on('click.intro', function() {
78799                                 // Watch for the combobox to appear...
78800                                 var watcher;
78801                                 watcher = window.setInterval(function() {
78802                                     if (!context.container().select('div.combobox').empty()) {
78803                                         window.clearInterval(watcher);
78804                                         continueTo(chooseDescriptionField);
78805                                     }
78806                                 }, 300);
78807                             });
78808                     }, 300);  // after "Add Field" visible
78809
78810                 }, 400);  // after editor pane visible
78811
78812                 context.on('exit.intro', function() {
78813                     return continueTo(searchPresets);
78814                 });
78815
78816                 function continueTo(nextStep) {
78817                     context.container().select('.inspector-wrap').on('wheel.intro', null);
78818                     context.container().select('.more-fields .combobox-input').on('click.intro', null);
78819                     context.on('exit.intro', null);
78820                     nextStep();
78821                 }
78822             }
78823
78824
78825             function chooseDescriptionField() {
78826                 if (!_areaID || !context.hasEntity(_areaID)) {
78827                     return addArea();
78828                 }
78829                 var ids = context.selectedIDs();
78830                 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _areaID) {
78831                     return searchPresets();
78832                 }
78833
78834                 if (!context.container().select('.form-field-description').empty()) {
78835                     return continueTo(describePlayground);
78836                 }
78837
78838                 // Make sure combobox is ready..
78839                 if (context.container().select('div.combobox').empty()) {
78840                     return continueTo(clickAddField);
78841                 }
78842                 // Watch for the combobox to go away..
78843                 var watcher;
78844                 watcher = window.setInterval(function() {
78845                     if (context.container().select('div.combobox').empty()) {
78846                         window.clearInterval(watcher);
78847                         timeout(function() {
78848                             if (context.container().select('.form-field-description').empty()) {
78849                                 continueTo(retryChooseDescription);
78850                             } else {
78851                                 continueTo(describePlayground);
78852                             }
78853                         }, 300);  // after description field added.
78854                     }
78855                 }, 300);
78856
78857                 reveal('div.combobox',
78858                     helpString('intro.areas.choose_field', { field: descriptionField.label() }),
78859                     { duration: 300 }
78860                 );
78861
78862                 context.on('exit.intro', function() {
78863                     return continueTo(searchPresets);
78864                 });
78865
78866                 function continueTo(nextStep) {
78867                     if (watcher) { window.clearInterval(watcher); }
78868                     context.on('exit.intro', null);
78869                     nextStep();
78870                 }
78871             }
78872
78873
78874             function describePlayground() {
78875                 if (!_areaID || !context.hasEntity(_areaID)) {
78876                     return addArea();
78877                 }
78878                 var ids = context.selectedIDs();
78879                 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _areaID) {
78880                     return searchPresets();
78881                 }
78882
78883                 // reset pane, in case user happened to change it..
78884                 context.container().select('.inspector-wrap .panewrap').style('right', '0%');
78885
78886                 if (context.container().select('.form-field-description').empty()) {
78887                     return continueTo(retryChooseDescription);
78888                 }
78889
78890                 context.on('exit.intro', function() {
78891                     continueTo(play);
78892                 });
78893
78894                 reveal('.entity-editor-pane',
78895                     helpString('intro.areas.describe_playground', { button: icon('#iD-icon-close', 'pre-text') }),
78896                     { duration: 300 }
78897                 );
78898
78899                 function continueTo(nextStep) {
78900                     context.on('exit.intro', null);
78901                     nextStep();
78902                 }
78903             }
78904
78905
78906             function retryChooseDescription() {
78907                 if (!_areaID || !context.hasEntity(_areaID)) {
78908                     return addArea();
78909                 }
78910                 var ids = context.selectedIDs();
78911                 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _areaID) {
78912                     return searchPresets();
78913                 }
78914
78915                 // reset pane, in case user happened to change it..
78916                 context.container().select('.inspector-wrap .panewrap').style('right', '0%');
78917
78918                 reveal('.entity-editor-pane',
78919                     helpString('intro.areas.retry_add_field', { field: descriptionField.label() }), {
78920                     buttonText: _t('intro.ok'),
78921                     buttonCallback: function() { continueTo(clickAddField); }
78922                 });
78923
78924                 context.on('exit.intro', function() {
78925                     return continueTo(searchPresets);
78926                 });
78927
78928                 function continueTo(nextStep) {
78929                     context.on('exit.intro', null);
78930                     nextStep();
78931                 }
78932             }
78933
78934
78935             function play() {
78936                 dispatch$1.call('done');
78937                 reveal('.ideditor',
78938                     helpString('intro.areas.play', { next: _t('intro.lines.title') }), {
78939                         tooltipBox: '.intro-nav-wrap .chapter-line',
78940                         buttonText: _t('intro.ok'),
78941                         buttonCallback: function() { reveal('.ideditor'); }
78942                     }
78943                 );
78944             }
78945
78946
78947             chapter.enter = function() {
78948                 addArea();
78949             };
78950
78951
78952             chapter.exit = function() {
78953                 timeouts.forEach(window.clearTimeout);
78954                 context.on('enter.intro exit.intro', null);
78955                 context.map().on('move.intro drawn.intro', null);
78956                 context.history().on('change.intro', null);
78957                 context.container().select('.inspector-wrap').on('wheel.intro', null);
78958                 context.container().select('.preset-search-input').on('keydown.intro keyup.intro', null);
78959                 context.container().select('.more-fields .combobox-input').on('click.intro', null);
78960             };
78961
78962
78963             chapter.restart = function() {
78964                 chapter.exit();
78965                 chapter.enter();
78966             };
78967
78968
78969             return utilRebind(chapter, dispatch$1, 'on');
78970         }
78971
78972         function uiIntroLine(context, reveal) {
78973             var dispatch$1 = dispatch('done');
78974             var timeouts = [];
78975             var _tulipRoadID = null;
78976             var flowerRoadID = 'w646';
78977             var tulipRoadStart = [-85.6297754121684, 41.95805253325314];
78978             var tulipRoadMidpoint = [-85.62975395449628, 41.95787501510204];
78979             var tulipRoadIntersection = [-85.62974496187628, 41.95742515554585];
78980             var roadCategory = _mainPresetIndex.item('category-road_minor');
78981             var residentialPreset = _mainPresetIndex.item('highway/residential');
78982             var woodRoadID = 'w525';
78983             var woodRoadEndID = 'n2862';
78984             var woodRoadAddNode = [-85.62390110349587, 41.95397111462291];
78985             var woodRoadDragEndpoint = [-85.623867390213, 41.95466987786487];
78986             var woodRoadDragMidpoint = [-85.62386254803509, 41.95430395953872];
78987             var washingtonStreetID = 'w522';
78988             var twelfthAvenueID = 'w1';
78989             var eleventhAvenueEndID = 'n3550';
78990             var twelfthAvenueEndID = 'n5';
78991             var _washingtonSegmentID = null;
78992             var eleventhAvenueEnd = context.entity(eleventhAvenueEndID).loc;
78993             var twelfthAvenueEnd = context.entity(twelfthAvenueEndID).loc;
78994             var deleteLinesLoc = [-85.6219395542764, 41.95228033922477];
78995             var twelfthAvenue = [-85.62219310052491, 41.952505413152956];
78996
78997
78998             var chapter = {
78999                 title: 'intro.lines.title'
79000             };
79001
79002
79003             function timeout(f, t) {
79004                 timeouts.push(window.setTimeout(f, t));
79005             }
79006
79007
79008             function eventCancel() {
79009                 event.stopPropagation();
79010                 event.preventDefault();
79011             }
79012
79013
79014             function addLine() {
79015                 context.enter(modeBrowse(context));
79016                 context.history().reset('initial');
79017
79018                 var msec = transitionTime(tulipRoadStart, context.map().center());
79019                 if (msec) { reveal(null, null, { duration: 0 }); }
79020                 context.map().centerZoomEase(tulipRoadStart, 18.5, msec);
79021
79022                 timeout(function() {
79023                     var tooltip = reveal('button.add-line',
79024                         helpString('intro.lines.add_line'));
79025
79026                     tooltip.selectAll('.popover-inner')
79027                         .insert('svg', 'span')
79028                         .attr('class', 'tooltip-illustration')
79029                         .append('use')
79030                         .attr('xlink:href', '#iD-graphic-lines');
79031
79032                     context.on('enter.intro', function(mode) {
79033                         if (mode.id !== 'add-line') { return; }
79034                         continueTo(startLine);
79035                     });
79036                 }, msec + 100);
79037
79038                 function continueTo(nextStep) {
79039                     context.on('enter.intro', null);
79040                     nextStep();
79041                 }
79042             }
79043
79044
79045             function startLine() {
79046                 if (context.mode().id !== 'add-line') { return chapter.restart(); }
79047
79048                 _tulipRoadID = null;
79049
79050                 var padding = 70 * Math.pow(2, context.map().zoom() - 18);
79051                 var box = pad(tulipRoadStart, padding, context);
79052                 box.height = box.height + 100;
79053
79054                 var textId = context.lastPointerType() === 'mouse' ? 'start_line' : 'start_line_tap';
79055                 var startLineString = helpString('intro.lines.missing_road') + '{br}' +
79056                     helpString('intro.lines.line_draw_info') +
79057                     helpString('intro.lines.' + textId);
79058                 reveal(box, startLineString);
79059
79060                 context.map().on('move.intro drawn.intro', function() {
79061                     padding = 70 * Math.pow(2, context.map().zoom() - 18);
79062                     box = pad(tulipRoadStart, padding, context);
79063                     box.height = box.height + 100;
79064                     reveal(box, startLineString, { duration: 0 });
79065                 });
79066
79067                 context.on('enter.intro', function(mode) {
79068                     if (mode.id !== 'draw-line') { return chapter.restart(); }
79069                     continueTo(drawLine);
79070                 });
79071
79072                 function continueTo(nextStep) {
79073                     context.map().on('move.intro drawn.intro', null);
79074                     context.on('enter.intro', null);
79075                     nextStep();
79076                 }
79077             }
79078
79079
79080             function drawLine() {
79081                 if (context.mode().id !== 'draw-line') { return chapter.restart(); }
79082
79083                 _tulipRoadID = context.mode().selectedIDs()[0];
79084                 context.map().centerEase(tulipRoadMidpoint, 500);
79085
79086                 timeout(function() {
79087                     var padding = 200 * Math.pow(2, context.map().zoom() - 18.5);
79088                     var box = pad(tulipRoadMidpoint, padding, context);
79089                     box.height = box.height * 2;
79090                     reveal(box,
79091                         helpString('intro.lines.intersect', { name: _t('intro.graph.name.flower-street') })
79092                     );
79093
79094                     context.map().on('move.intro drawn.intro', function() {
79095                         padding = 200 * Math.pow(2, context.map().zoom() - 18.5);
79096                         box = pad(tulipRoadMidpoint, padding, context);
79097                         box.height = box.height * 2;
79098                         reveal(box,
79099                             helpString('intro.lines.intersect', { name: _t('intro.graph.name.flower-street') }),
79100                             { duration: 0 }
79101                         );
79102                     });
79103                 }, 550);  // after easing..
79104
79105                 context.history().on('change.intro', function() {
79106                     if (isLineConnected()) {
79107                         continueTo(continueLine);
79108                     }
79109                 });
79110
79111                 context.on('enter.intro', function(mode) {
79112                     if (mode.id === 'draw-line') {
79113                         return;
79114                     } else if (mode.id === 'select') {
79115                         continueTo(retryIntersect);
79116                         return;
79117                     } else {
79118                         return chapter.restart();
79119                     }
79120                 });
79121
79122                 function continueTo(nextStep) {
79123                     context.map().on('move.intro drawn.intro', null);
79124                     context.history().on('change.intro', null);
79125                     context.on('enter.intro', null);
79126                     nextStep();
79127                 }
79128             }
79129
79130
79131             function isLineConnected() {
79132                 var entity = _tulipRoadID && context.hasEntity(_tulipRoadID);
79133                 if (!entity) { return false; }
79134
79135                 var drawNodes = context.graph().childNodes(entity);
79136                 return drawNodes.some(function(node) {
79137                     return context.graph().parentWays(node).some(function(parent) {
79138                         return parent.id === flowerRoadID;
79139                     });
79140                 });
79141             }
79142
79143
79144             function retryIntersect() {
79145                 select(window).on('pointerdown.intro mousedown.intro', eventCancel, true);
79146
79147                 var box = pad(tulipRoadIntersection, 80, context);
79148                 reveal(box,
79149                     helpString('intro.lines.retry_intersect', { name: _t('intro.graph.name.flower-street') })
79150                 );
79151
79152                 timeout(chapter.restart, 3000);
79153             }
79154
79155
79156             function continueLine() {
79157                 if (context.mode().id !== 'draw-line') { return chapter.restart(); }
79158                 var entity = _tulipRoadID && context.hasEntity(_tulipRoadID);
79159                 if (!entity) { return chapter.restart(); }
79160
79161                 context.map().centerEase(tulipRoadIntersection, 500);
79162
79163                 var continueLineText = helpString('intro.lines.continue_line') + '{br}' +
79164                     helpString('intro.lines.finish_line_' + (context.lastPointerType() === 'mouse' ? 'click' : 'tap')) +
79165                     helpString('intro.lines.finish_road');
79166
79167                 reveal('.surface', continueLineText);
79168
79169                 context.on('enter.intro', function(mode) {
79170                     if (mode.id === 'draw-line')
79171                         { return; }
79172                     else if (mode.id === 'select')
79173                         { return continueTo(chooseCategoryRoad); }
79174                     else
79175                         { return chapter.restart(); }
79176                 });
79177
79178                 function continueTo(nextStep) {
79179                     context.on('enter.intro', null);
79180                     nextStep();
79181                 }
79182             }
79183
79184
79185             function chooseCategoryRoad() {
79186                 if (context.mode().id !== 'select') { return chapter.restart(); }
79187
79188                 context.on('exit.intro', function() {
79189                     return chapter.restart();
79190                 });
79191
79192                 var button = context.container().select('.preset-category-road_minor .preset-list-button');
79193                 if (button.empty()) { return chapter.restart(); }
79194
79195                 // disallow scrolling
79196                 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
79197
79198                 timeout(function() {
79199                     // reset pane, in case user somehow happened to change it..
79200                     context.container().select('.inspector-wrap .panewrap').style('right', '-100%');
79201
79202                     reveal(button.node(),
79203                         helpString('intro.lines.choose_category_road', { category: roadCategory.name() })
79204                     );
79205
79206                     button.on('click.intro', function() {
79207                         continueTo(choosePresetResidential);
79208                     });
79209
79210                 }, 400);  // after editor pane visible
79211
79212                 function continueTo(nextStep) {
79213                     context.container().select('.inspector-wrap').on('wheel.intro', null);
79214                     context.container().select('.preset-list-button').on('click.intro', null);
79215                     context.on('exit.intro', null);
79216                     nextStep();
79217                 }
79218             }
79219
79220
79221             function choosePresetResidential() {
79222                 if (context.mode().id !== 'select') { return chapter.restart(); }
79223
79224                 context.on('exit.intro', function() {
79225                     return chapter.restart();
79226                 });
79227
79228                 var subgrid = context.container().select('.preset-category-road_minor .subgrid');
79229                 if (subgrid.empty()) { return chapter.restart(); }
79230
79231                 subgrid.selectAll(':not(.preset-highway-residential) .preset-list-button')
79232                     .on('click.intro', function() {
79233                         continueTo(retryPresetResidential);
79234                     });
79235
79236                 subgrid.selectAll('.preset-highway-residential .preset-list-button')
79237                     .on('click.intro', function() {
79238                         continueTo(nameRoad);
79239                     });
79240
79241                 timeout(function() {
79242                     reveal(subgrid.node(),
79243                         helpString('intro.lines.choose_preset_residential', { preset: residentialPreset.name() }),
79244                         { tooltipBox: '.preset-highway-residential .preset-list-button', duration: 300 }
79245                     );
79246                 }, 300);
79247
79248                 function continueTo(nextStep) {
79249                     context.container().select('.preset-list-button').on('click.intro', null);
79250                     context.on('exit.intro', null);
79251                     nextStep();
79252                 }
79253             }
79254
79255
79256             // selected wrong road type
79257             function retryPresetResidential() {
79258                 if (context.mode().id !== 'select') { return chapter.restart(); }
79259
79260                 context.on('exit.intro', function() {
79261                     return chapter.restart();
79262                 });
79263
79264                 // disallow scrolling
79265                 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
79266
79267                 timeout(function() {
79268                     var button = context.container().select('.entity-editor-pane .preset-list-button');
79269
79270                     reveal(button.node(),
79271                         helpString('intro.lines.retry_preset_residential', { preset: residentialPreset.name() })
79272                     );
79273
79274                     button.on('click.intro', function() {
79275                         continueTo(chooseCategoryRoad);
79276                     });
79277
79278                 }, 500);
79279
79280                 function continueTo(nextStep) {
79281                     context.container().select('.inspector-wrap').on('wheel.intro', null);
79282                     context.container().select('.preset-list-button').on('click.intro', null);
79283                     context.on('exit.intro', null);
79284                     nextStep();
79285                 }
79286             }
79287
79288
79289             function nameRoad() {
79290                 context.on('exit.intro', function() {
79291                     continueTo(didNameRoad);
79292                 });
79293
79294                 timeout(function() {
79295                     reveal('.entity-editor-pane',
79296                         helpString('intro.lines.name_road', { button: icon('#iD-icon-close', 'pre-text') }),
79297                         { tooltipClass: 'intro-lines-name_road' }
79298                     );
79299                 }, 500);
79300
79301                 function continueTo(nextStep) {
79302                     context.on('exit.intro', null);
79303                     nextStep();
79304                 }
79305             }
79306
79307
79308             function didNameRoad() {
79309                 context.history().checkpoint('doneAddLine');
79310
79311                 timeout(function() {
79312                     reveal('.surface', helpString('intro.lines.did_name_road'), {
79313                         buttonText: _t('intro.ok'),
79314                         buttonCallback: function() { continueTo(updateLine); }
79315                     });
79316                 }, 500);
79317
79318                 function continueTo(nextStep) {
79319                     nextStep();
79320                 }
79321             }
79322
79323
79324             function updateLine() {
79325                 context.history().reset('doneAddLine');
79326                 if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
79327                     return chapter.restart();
79328                 }
79329
79330                 var msec = transitionTime(woodRoadDragMidpoint, context.map().center());
79331                 if (msec) { reveal(null, null, { duration: 0 }); }
79332                 context.map().centerZoomEase(woodRoadDragMidpoint, 19, msec);
79333
79334                 timeout(function() {
79335                     var padding = 250 * Math.pow(2, context.map().zoom() - 19);
79336                     var box = pad(woodRoadDragMidpoint, padding, context);
79337                     var advance = function() { continueTo(addNode); };
79338
79339                     reveal(box, helpString('intro.lines.update_line'),
79340                         { buttonText: _t('intro.ok'), buttonCallback: advance }
79341                     );
79342
79343                     context.map().on('move.intro drawn.intro', function() {
79344                         var padding = 250 * Math.pow(2, context.map().zoom() - 19);
79345                         var box = pad(woodRoadDragMidpoint, padding, context);
79346                         reveal(box, helpString('intro.lines.update_line'),
79347                             { duration: 0, buttonText: _t('intro.ok'), buttonCallback: advance }
79348                         );
79349                     });
79350                 }, msec + 100);
79351
79352                 function continueTo(nextStep) {
79353                     context.map().on('move.intro drawn.intro', null);
79354                     nextStep();
79355                 }
79356             }
79357
79358
79359             function addNode() {
79360                 context.history().reset('doneAddLine');
79361                 if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
79362                     return chapter.restart();
79363                 }
79364
79365                 var padding = 40 * Math.pow(2, context.map().zoom() - 19);
79366                 var box = pad(woodRoadAddNode, padding, context);
79367                 var addNodeString = helpString('intro.lines.add_node' + (context.lastPointerType() === 'mouse' ? '' : '_touch'));
79368                 reveal(box, addNodeString);
79369
79370                 context.map().on('move.intro drawn.intro', function() {
79371                     var padding = 40 * Math.pow(2, context.map().zoom() - 19);
79372                     var box = pad(woodRoadAddNode, padding, context);
79373                     reveal(box, addNodeString, { duration: 0 });
79374                 });
79375
79376                 context.history().on('change.intro', function(changed) {
79377                     if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
79378                         return continueTo(updateLine);
79379                     }
79380                     if (changed.created().length === 1) {
79381                         timeout(function() { continueTo(startDragEndpoint); }, 500);
79382                     }
79383                 });
79384
79385                 context.on('enter.intro', function(mode) {
79386                     if (mode.id !== 'select') {
79387                         continueTo(updateLine);
79388                     }
79389                 });
79390
79391                 function continueTo(nextStep) {
79392                     context.map().on('move.intro drawn.intro', null);
79393                     context.history().on('change.intro', null);
79394                     context.on('enter.intro', null);
79395                     nextStep();
79396                 }
79397             }
79398
79399
79400             function startDragEndpoint() {
79401                 if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
79402                     return continueTo(updateLine);
79403                 }
79404                 var padding = 100 * Math.pow(2, context.map().zoom() - 19);
79405                 var box = pad(woodRoadDragEndpoint, padding, context);
79406                 var startDragString = helpString('intro.lines.start_drag_endpoint' + (context.lastPointerType() === 'mouse' ? '' : '_touch')) +
79407                     helpString('intro.lines.drag_to_intersection');
79408                 reveal(box, startDragString);
79409
79410                 context.map().on('move.intro drawn.intro', function() {
79411                     if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
79412                         return continueTo(updateLine);
79413                     }
79414                     var padding = 100 * Math.pow(2, context.map().zoom() - 19);
79415                     var box = pad(woodRoadDragEndpoint, padding, context);
79416                     reveal(box, startDragString, { duration: 0 });
79417
79418                     var entity = context.entity(woodRoadEndID);
79419                     if (geoSphericalDistance(entity.loc, woodRoadDragEndpoint) <= 4) {
79420                         continueTo(finishDragEndpoint);
79421                     }
79422                 });
79423
79424                 function continueTo(nextStep) {
79425                     context.map().on('move.intro drawn.intro', null);
79426                     nextStep();
79427                 }
79428             }
79429
79430
79431             function finishDragEndpoint() {
79432                 if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
79433                     return continueTo(updateLine);
79434                 }
79435
79436                 var padding = 100 * Math.pow(2, context.map().zoom() - 19);
79437                 var box = pad(woodRoadDragEndpoint, padding, context);
79438                 var finishDragString = helpString('intro.lines.spot_looks_good') +
79439                     helpString('intro.lines.finish_drag_endpoint' + (context.lastPointerType() === 'mouse' ? '' : '_touch'));
79440                 reveal(box, finishDragString);
79441
79442                 context.map().on('move.intro drawn.intro', function() {
79443                     if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
79444                         return continueTo(updateLine);
79445                     }
79446                     var padding = 100 * Math.pow(2, context.map().zoom() - 19);
79447                     var box = pad(woodRoadDragEndpoint, padding, context);
79448                     reveal(box, finishDragString, { duration: 0 });
79449
79450                     var entity = context.entity(woodRoadEndID);
79451                     if (geoSphericalDistance(entity.loc, woodRoadDragEndpoint) > 4) {
79452                         continueTo(startDragEndpoint);
79453                     }
79454                 });
79455
79456                 context.on('enter.intro', function() {
79457                     continueTo(startDragMidpoint);
79458                 });
79459
79460                 function continueTo(nextStep) {
79461                     context.map().on('move.intro drawn.intro', null);
79462                     context.on('enter.intro', null);
79463                     nextStep();
79464                 }
79465             }
79466
79467
79468             function startDragMidpoint() {
79469                 if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
79470                     return continueTo(updateLine);
79471                 }
79472                 if (context.selectedIDs().indexOf(woodRoadID) === -1) {
79473                     context.enter(modeSelect(context, [woodRoadID]));
79474                 }
79475
79476                 var padding = 80 * Math.pow(2, context.map().zoom() - 19);
79477                 var box = pad(woodRoadDragMidpoint, padding, context);
79478                 reveal(box, helpString('intro.lines.start_drag_midpoint'));
79479
79480                 context.map().on('move.intro drawn.intro', function() {
79481                     if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
79482                         return continueTo(updateLine);
79483                     }
79484                     var padding = 80 * Math.pow(2, context.map().zoom() - 19);
79485                     var box = pad(woodRoadDragMidpoint, padding, context);
79486                     reveal(box, helpString('intro.lines.start_drag_midpoint'), { duration: 0 });
79487                 });
79488
79489                 context.history().on('change.intro', function(changed) {
79490                     if (changed.created().length === 1) {
79491                         continueTo(continueDragMidpoint);
79492                     }
79493                 });
79494
79495                 context.on('enter.intro', function(mode) {
79496                     if (mode.id !== 'select') {
79497                         // keep Wood Road selected so midpoint triangles are drawn..
79498                         context.enter(modeSelect(context, [woodRoadID]));
79499                     }
79500                 });
79501
79502                 function continueTo(nextStep) {
79503                     context.map().on('move.intro drawn.intro', null);
79504                     context.history().on('change.intro', null);
79505                     context.on('enter.intro', null);
79506                     nextStep();
79507                 }
79508             }
79509
79510
79511             function continueDragMidpoint() {
79512                 if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
79513                     return continueTo(updateLine);
79514                 }
79515
79516                 var padding = 100 * Math.pow(2, context.map().zoom() - 19);
79517                 var box = pad(woodRoadDragEndpoint, padding, context);
79518                 box.height += 400;
79519
79520                 var advance = function() {
79521                     context.history().checkpoint('doneUpdateLine');
79522                     continueTo(deleteLines);
79523                 };
79524
79525                 reveal(box, helpString('intro.lines.continue_drag_midpoint'),
79526                     { buttonText: _t('intro.ok'), buttonCallback: advance }
79527                 );
79528
79529                 context.map().on('move.intro drawn.intro', function() {
79530                     if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
79531                         return continueTo(updateLine);
79532                     }
79533                     var padding = 100 * Math.pow(2, context.map().zoom() - 19);
79534                     var box = pad(woodRoadDragEndpoint, padding, context);
79535                     box.height += 400;
79536                     reveal(box, helpString('intro.lines.continue_drag_midpoint'),
79537                         { duration: 0, buttonText: _t('intro.ok'), buttonCallback: advance }
79538                     );
79539                 });
79540
79541                 function continueTo(nextStep) {
79542                     context.map().on('move.intro drawn.intro', null);
79543                     nextStep();
79544                 }
79545             }
79546
79547
79548             function deleteLines() {
79549                 context.history().reset('doneUpdateLine');
79550                 context.enter(modeBrowse(context));
79551
79552                 if (!context.hasEntity(washingtonStreetID) ||
79553                     !context.hasEntity(twelfthAvenueID) ||
79554                     !context.hasEntity(eleventhAvenueEndID)) {
79555                     return chapter.restart();
79556                 }
79557
79558                 var msec = transitionTime(deleteLinesLoc, context.map().center());
79559                 if (msec) { reveal(null, null, { duration: 0 }); }
79560                 context.map().centerZoomEase(deleteLinesLoc, 18, msec);
79561
79562                 timeout(function() {
79563                     var padding = 200 * Math.pow(2, context.map().zoom() - 18);
79564                     var box = pad(deleteLinesLoc, padding, context);
79565                     box.top -= 200;
79566                     box.height += 400;
79567                     var advance = function() { continueTo(rightClickIntersection); };
79568
79569                     reveal(box, helpString('intro.lines.delete_lines', { street: _t('intro.graph.name.12th-avenue') }),
79570                         { buttonText: _t('intro.ok'), buttonCallback: advance }
79571                     );
79572
79573                     context.map().on('move.intro drawn.intro', function() {
79574                         var padding = 200 * Math.pow(2, context.map().zoom() - 18);
79575                         var box = pad(deleteLinesLoc, padding, context);
79576                         box.top -= 200;
79577                         box.height += 400;
79578                         reveal(box, helpString('intro.lines.delete_lines', { street: _t('intro.graph.name.12th-avenue') }),
79579                             { duration: 0, buttonText: _t('intro.ok'), buttonCallback: advance }
79580                         );
79581                     });
79582
79583                     context.history().on('change.intro', function() {
79584                         timeout(function() {
79585                             continueTo(deleteLines);
79586                         }, 500);  // after any transition (e.g. if user deleted intersection)
79587                     });
79588
79589                 }, msec + 100);
79590
79591                 function continueTo(nextStep) {
79592                     context.map().on('move.intro drawn.intro', null);
79593                     context.history().on('change.intro', null);
79594                     nextStep();
79595                 }
79596             }
79597
79598
79599             function rightClickIntersection() {
79600                 context.history().reset('doneUpdateLine');
79601                 context.enter(modeBrowse(context));
79602
79603                 context.map().centerZoomEase(eleventhAvenueEnd, 18, 500);
79604
79605                 var rightClickString = helpString('intro.lines.split_street', {
79606                         street1: _t('intro.graph.name.11th-avenue'),
79607                         street2: _t('intro.graph.name.washington-street')
79608                     }) +
79609                     helpString('intro.lines.' + (context.lastPointerType() === 'mouse' ? 'rightclick_intersection' : 'edit_menu_intersection_touch'));
79610
79611                 timeout(function() {
79612                     var padding = 60 * Math.pow(2, context.map().zoom() - 18);
79613                     var box = pad(eleventhAvenueEnd, padding, context);
79614                     reveal(box, rightClickString);
79615
79616                     context.map().on('move.intro drawn.intro', function() {
79617                         var padding = 60 * Math.pow(2, context.map().zoom() - 18);
79618                         var box = pad(eleventhAvenueEnd, padding, context);
79619                         reveal(box, rightClickString,
79620                             { duration: 0 }
79621                         );
79622                     });
79623
79624                     context.on('enter.intro', function(mode) {
79625                         if (mode.id !== 'select') { return; }
79626                         var ids = context.selectedIDs();
79627                         if (ids.length !== 1 || ids[0] !== eleventhAvenueEndID) { return; }
79628
79629                         timeout(function() {
79630                             var node = selectMenuItem(context, 'split').node();
79631                             if (!node) { return; }
79632                             continueTo(splitIntersection);
79633                         }, 50);  // after menu visible
79634                     });
79635
79636                     context.history().on('change.intro', function() {
79637                         timeout(function() {
79638                             continueTo(deleteLines);
79639                         }, 300);  // after any transition (e.g. if user deleted intersection)
79640                     });
79641
79642                 }, 600);
79643
79644                 function continueTo(nextStep) {
79645                     context.map().on('move.intro drawn.intro', null);
79646                     context.on('enter.intro', null);
79647                     context.history().on('change.intro', null);
79648                     nextStep();
79649                 }
79650             }
79651
79652
79653             function splitIntersection() {
79654                 if (!context.hasEntity(washingtonStreetID) ||
79655                     !context.hasEntity(twelfthAvenueID) ||
79656                     !context.hasEntity(eleventhAvenueEndID)) {
79657                     return continueTo(deleteLines);
79658                 }
79659
79660                 var node = selectMenuItem(context, 'split').node();
79661                 if (!node) { return continueTo(rightClickIntersection); }
79662
79663                 var wasChanged = false;
79664                 _washingtonSegmentID = null;
79665
79666                 reveal('.edit-menu', helpString('intro.lines.split_intersection',
79667                     { street: _t('intro.graph.name.washington-street') }),
79668                     { padding: 50 }
79669                 );
79670
79671                 context.map().on('move.intro drawn.intro', function() {
79672                     var node = selectMenuItem(context, 'split').node();
79673                     if (!wasChanged && !node) { return continueTo(rightClickIntersection); }
79674
79675                     reveal('.edit-menu', helpString('intro.lines.split_intersection',
79676                         { street: _t('intro.graph.name.washington-street') }),
79677                         { duration: 0, padding: 50 }
79678                     );
79679                 });
79680
79681                 context.history().on('change.intro', function(changed) {
79682                     wasChanged = true;
79683                     timeout(function() {
79684                         if (context.history().undoAnnotation() === _t('operations.split.annotation.line')) {
79685                             _washingtonSegmentID = changed.created()[0].id;
79686                             continueTo(didSplit);
79687                         } else {
79688                             _washingtonSegmentID = null;
79689                             continueTo(retrySplit);
79690                         }
79691                     }, 300);  // after any transition (e.g. if user deleted intersection)
79692                 });
79693
79694                 function continueTo(nextStep) {
79695                     context.map().on('move.intro drawn.intro', null);
79696                     context.history().on('change.intro', null);
79697                     nextStep();
79698                 }
79699             }
79700
79701
79702             function retrySplit() {
79703                 context.enter(modeBrowse(context));
79704                 context.map().centerZoomEase(eleventhAvenueEnd, 18, 500);
79705                 var advance = function() { continueTo(rightClickIntersection); };
79706
79707                 var padding = 60 * Math.pow(2, context.map().zoom() - 18);
79708                 var box = pad(eleventhAvenueEnd, padding, context);
79709                 reveal(box, helpString('intro.lines.retry_split'),
79710                     { buttonText: _t('intro.ok'), buttonCallback: advance }
79711                 );
79712
79713                 context.map().on('move.intro drawn.intro', function() {
79714                     var padding = 60 * Math.pow(2, context.map().zoom() - 18);
79715                     var box = pad(eleventhAvenueEnd, padding, context);
79716                     reveal(box, helpString('intro.lines.retry_split'),
79717                         { duration: 0, buttonText: _t('intro.ok'), buttonCallback: advance }
79718                     );
79719                 });
79720
79721                 function continueTo(nextStep) {
79722                     context.map().on('move.intro drawn.intro', null);
79723                     nextStep();
79724                 }
79725             }
79726
79727
79728             function didSplit() {
79729                 if (!_washingtonSegmentID ||
79730                     !context.hasEntity(_washingtonSegmentID) ||
79731                     !context.hasEntity(washingtonStreetID) ||
79732                     !context.hasEntity(twelfthAvenueID) ||
79733                     !context.hasEntity(eleventhAvenueEndID)) {
79734                     return continueTo(rightClickIntersection);
79735                 }
79736
79737                 var ids = context.selectedIDs();
79738                 var string = 'intro.lines.did_split_' + (ids.length > 1 ? 'multi' : 'single');
79739                 var street = _t('intro.graph.name.washington-street');
79740
79741                 var padding = 200 * Math.pow(2, context.map().zoom() - 18);
79742                 var box = pad(twelfthAvenue, padding, context);
79743                 box.width = box.width / 2;
79744                 reveal(box, helpString(string, { street1: street, street2: street }),
79745                     { duration: 500 }
79746                 );
79747
79748                 timeout(function() {
79749                     context.map().centerZoomEase(twelfthAvenue, 18, 500);
79750
79751                     context.map().on('move.intro drawn.intro', function() {
79752                         var padding = 200 * Math.pow(2, context.map().zoom() - 18);
79753                         var box = pad(twelfthAvenue, padding, context);
79754                         box.width = box.width / 2;
79755                         reveal(box, helpString(string, { street1: street, street2: street }),
79756                             { duration: 0 }
79757                         );
79758                     });
79759                 }, 600);  // after initial reveal and curtain cut
79760
79761                 context.on('enter.intro', function() {
79762                     var ids = context.selectedIDs();
79763                     if (ids.length === 1 && ids[0] === _washingtonSegmentID) {
79764                         continueTo(multiSelect);
79765                     }
79766                 });
79767
79768                 context.history().on('change.intro', function() {
79769                     if (!_washingtonSegmentID ||
79770                         !context.hasEntity(_washingtonSegmentID) ||
79771                         !context.hasEntity(washingtonStreetID) ||
79772                         !context.hasEntity(twelfthAvenueID) ||
79773                         !context.hasEntity(eleventhAvenueEndID)) {
79774                         return continueTo(rightClickIntersection);
79775                     }
79776                 });
79777
79778                 function continueTo(nextStep) {
79779                     context.map().on('move.intro drawn.intro', null);
79780                     context.on('enter.intro', null);
79781                     context.history().on('change.intro', null);
79782                     nextStep();
79783                 }
79784             }
79785
79786
79787             function multiSelect() {
79788                 if (!_washingtonSegmentID ||
79789                     !context.hasEntity(_washingtonSegmentID) ||
79790                     !context.hasEntity(washingtonStreetID) ||
79791                     !context.hasEntity(twelfthAvenueID) ||
79792                     !context.hasEntity(eleventhAvenueEndID)) {
79793                     return continueTo(rightClickIntersection);
79794                 }
79795
79796                 var ids = context.selectedIDs();
79797                 var hasWashington = ids.indexOf(_washingtonSegmentID) !== -1;
79798                 var hasTwelfth = ids.indexOf(twelfthAvenueID) !== -1;
79799
79800                 if (hasWashington && hasTwelfth) {
79801                     return continueTo(multiRightClick);
79802                 } else if (!hasWashington && !hasTwelfth) {
79803                     return continueTo(didSplit);
79804                 }
79805
79806                 context.map().centerZoomEase(twelfthAvenue, 18, 500);
79807
79808                 timeout(function() {
79809                     var selected, other, padding, box;
79810                     if (hasWashington) {
79811                         selected = _t('intro.graph.name.washington-street');
79812                         other = _t('intro.graph.name.12th-avenue');
79813                         padding = 60 * Math.pow(2, context.map().zoom() - 18);
79814                         box = pad(twelfthAvenueEnd, padding, context);
79815                         box.width *= 3;
79816                     } else {
79817                         selected = _t('intro.graph.name.12th-avenue');
79818                         other = _t('intro.graph.name.washington-street');
79819                         padding = 200 * Math.pow(2, context.map().zoom() - 18);
79820                         box = pad(twelfthAvenue, padding, context);
79821                         box.width /= 2;
79822                     }
79823
79824                     reveal(box,
79825                         helpString('intro.lines.multi_select',
79826                             { selected: selected, other1: other }) + ' ' +
79827                         helpString('intro.lines.add_to_selection_' + (context.lastPointerType() === 'mouse' ? 'click' : 'touch'),
79828                             { selected: selected, other2: other })
79829                     );
79830
79831                     context.map().on('move.intro drawn.intro', function() {
79832                         if (hasWashington) {
79833                             selected = _t('intro.graph.name.washington-street');
79834                             other = _t('intro.graph.name.12th-avenue');
79835                             padding = 60 * Math.pow(2, context.map().zoom() - 18);
79836                             box = pad(twelfthAvenueEnd, padding, context);
79837                             box.width *= 3;
79838                         } else {
79839                             selected = _t('intro.graph.name.12th-avenue');
79840                             other = _t('intro.graph.name.washington-street');
79841                             padding = 200 * Math.pow(2, context.map().zoom() - 18);
79842                             box = pad(twelfthAvenue, padding, context);
79843                             box.width /= 2;
79844                         }
79845
79846                         reveal(box,
79847                             helpString('intro.lines.multi_select',
79848                                 { selected: selected, other1: other }) + ' ' +
79849                             helpString('intro.lines.add_to_selection_' + (context.lastPointerType() === 'mouse' ? 'click' : 'touch'),
79850                                 { selected: selected, other2: other }),
79851                             { duration: 0 }
79852                         );
79853                     });
79854
79855                     context.on('enter.intro', function() {
79856                         continueTo(multiSelect);
79857                     });
79858
79859                     context.history().on('change.intro', function() {
79860                         if (!_washingtonSegmentID ||
79861                             !context.hasEntity(_washingtonSegmentID) ||
79862                             !context.hasEntity(washingtonStreetID) ||
79863                             !context.hasEntity(twelfthAvenueID) ||
79864                             !context.hasEntity(eleventhAvenueEndID)) {
79865                             return continueTo(rightClickIntersection);
79866                         }
79867                     });
79868                 }, 600);
79869
79870                 function continueTo(nextStep) {
79871                     context.map().on('move.intro drawn.intro', null);
79872                     context.on('enter.intro', null);
79873                     context.history().on('change.intro', null);
79874                     nextStep();
79875                 }
79876             }
79877
79878
79879             function multiRightClick() {
79880                 if (!_washingtonSegmentID ||
79881                     !context.hasEntity(_washingtonSegmentID) ||
79882                     !context.hasEntity(washingtonStreetID) ||
79883                     !context.hasEntity(twelfthAvenueID) ||
79884                     !context.hasEntity(eleventhAvenueEndID)) {
79885                     return continueTo(rightClickIntersection);
79886                 }
79887
79888                 var padding = 200 * Math.pow(2, context.map().zoom() - 18);
79889                 var box = pad(twelfthAvenue, padding, context);
79890
79891                 var rightClickString = helpString('intro.lines.multi_select_success') +
79892                     helpString('intro.lines.multi_' + (context.lastPointerType() === 'mouse' ? 'rightclick' : 'edit_menu_touch'));
79893                 reveal(box, rightClickString);
79894
79895                 context.map().on('move.intro drawn.intro', function() {
79896                     var padding = 200 * Math.pow(2, context.map().zoom() - 18);
79897                     var box = pad(twelfthAvenue, padding, context);
79898                     reveal(box, rightClickString, { duration: 0 });
79899                 });
79900
79901                 context.ui().editMenu().on('toggled.intro', function(open) {
79902                     if (!open) { return; }
79903
79904                     timeout(function() {
79905                         var ids = context.selectedIDs();
79906                         if (ids.length === 2 &&
79907                             ids.indexOf(twelfthAvenueID) !== -1 &&
79908                             ids.indexOf(_washingtonSegmentID) !== -1) {
79909                                 var node = selectMenuItem(context, 'delete').node();
79910                                 if (!node) { return; }
79911                                 continueTo(multiDelete);
79912                         } else if (ids.length === 1 &&
79913                             ids.indexOf(_washingtonSegmentID) !== -1) {
79914                             return continueTo(multiSelect);
79915                         } else {
79916                             return continueTo(didSplit);
79917                         }
79918                     }, 300);  // after edit menu visible
79919                 });
79920
79921                 context.history().on('change.intro', function() {
79922                     if (!_washingtonSegmentID ||
79923                         !context.hasEntity(_washingtonSegmentID) ||
79924                         !context.hasEntity(washingtonStreetID) ||
79925                         !context.hasEntity(twelfthAvenueID) ||
79926                         !context.hasEntity(eleventhAvenueEndID)) {
79927                         return continueTo(rightClickIntersection);
79928                     }
79929                 });
79930
79931                 function continueTo(nextStep) {
79932                     context.map().on('move.intro drawn.intro', null);
79933                     context.ui().editMenu().on('toggled.intro', null);
79934                     context.history().on('change.intro', null);
79935                     nextStep();
79936                 }
79937             }
79938
79939
79940             function multiDelete() {
79941                 if (!_washingtonSegmentID ||
79942                     !context.hasEntity(_washingtonSegmentID) ||
79943                     !context.hasEntity(washingtonStreetID) ||
79944                     !context.hasEntity(twelfthAvenueID) ||
79945                     !context.hasEntity(eleventhAvenueEndID)) {
79946                     return continueTo(rightClickIntersection);
79947                 }
79948
79949                 var node = selectMenuItem(context, 'delete').node();
79950                 if (!node) { return continueTo(multiRightClick); }
79951
79952                 reveal('.edit-menu',
79953                     helpString('intro.lines.multi_delete'),
79954                     { padding: 50 }
79955                 );
79956
79957                 context.map().on('move.intro drawn.intro', function() {
79958                     reveal('.edit-menu',
79959                         helpString('intro.lines.multi_delete'),
79960                         { duration: 0, padding: 50 }
79961                     );
79962                 });
79963
79964                 context.on('exit.intro', function() {
79965                     if (context.hasEntity(_washingtonSegmentID) || context.hasEntity(twelfthAvenueID)) {
79966                         return continueTo(multiSelect);  // left select mode but roads still exist
79967                     }
79968                 });
79969
79970                 context.history().on('change.intro', function() {
79971                     if (context.hasEntity(_washingtonSegmentID) || context.hasEntity(twelfthAvenueID)) {
79972                         continueTo(retryDelete);         // changed something but roads still exist
79973                     } else {
79974                         continueTo(play);
79975                     }
79976                 });
79977
79978                 function continueTo(nextStep) {
79979                     context.map().on('move.intro drawn.intro', null);
79980                     context.on('exit.intro', null);
79981                     context.history().on('change.intro', null);
79982                     nextStep();
79983                 }
79984             }
79985
79986
79987             function retryDelete() {
79988                 context.enter(modeBrowse(context));
79989
79990                 var padding = 200 * Math.pow(2, context.map().zoom() - 18);
79991                 var box = pad(twelfthAvenue, padding, context);
79992                 reveal(box, helpString('intro.lines.retry_delete'), {
79993                     buttonText: _t('intro.ok'),
79994                     buttonCallback: function() { continueTo(multiSelect); }
79995                 });
79996
79997                 function continueTo(nextStep) {
79998                     nextStep();
79999                 }
80000             }
80001
80002
80003             function play() {
80004                 dispatch$1.call('done');
80005                 reveal('.ideditor',
80006                     helpString('intro.lines.play', { next: _t('intro.buildings.title') }), {
80007                         tooltipBox: '.intro-nav-wrap .chapter-building',
80008                         buttonText: _t('intro.ok'),
80009                         buttonCallback: function() { reveal('.ideditor'); }
80010                     }
80011                 );
80012            }
80013
80014
80015             chapter.enter = function() {
80016                 addLine();
80017             };
80018
80019
80020             chapter.exit = function() {
80021                 timeouts.forEach(window.clearTimeout);
80022                 select(window).on('pointerdown.intro mousedown.intro', null, true);
80023                 context.on('enter.intro exit.intro', null);
80024                 context.map().on('move.intro drawn.intro', null);
80025                 context.history().on('change.intro', null);
80026                 context.container().select('.inspector-wrap').on('wheel.intro', null);
80027                 context.container().select('.preset-list-button').on('click.intro', null);
80028             };
80029
80030
80031             chapter.restart = function() {
80032                 chapter.exit();
80033                 chapter.enter();
80034             };
80035
80036
80037             return utilRebind(chapter, dispatch$1, 'on');
80038         }
80039
80040         function uiIntroBuilding(context, reveal) {
80041             var dispatch$1 = dispatch('done');
80042             var house = [-85.62815, 41.95638];
80043             var tank = [-85.62732, 41.95347];
80044             var buildingCatetory = _mainPresetIndex.item('category-building');
80045             var housePreset = _mainPresetIndex.item('building/house');
80046             var tankPreset = _mainPresetIndex.item('man_made/storage_tank');
80047             var timeouts = [];
80048             var _houseID = null;
80049             var _tankID = null;
80050
80051
80052             var chapter = {
80053                 title: 'intro.buildings.title'
80054             };
80055
80056
80057             function timeout(f, t) {
80058                 timeouts.push(window.setTimeout(f, t));
80059             }
80060
80061
80062             function eventCancel() {
80063                 event.stopPropagation();
80064                 event.preventDefault();
80065             }
80066
80067
80068             function revealHouse(center, text, options) {
80069                 var padding = 160 * Math.pow(2, context.map().zoom() - 20);
80070                 var box = pad(center, padding, context);
80071                 reveal(box, text, options);
80072             }
80073
80074
80075             function revealTank(center, text, options) {
80076                 var padding = 190 * Math.pow(2, context.map().zoom() - 19.5);
80077                 var box = pad(center, padding, context);
80078                 reveal(box, text, options);
80079             }
80080
80081
80082             function addHouse() {
80083                 context.enter(modeBrowse(context));
80084                 context.history().reset('initial');
80085                 _houseID = null;
80086
80087                 var msec = transitionTime(house, context.map().center());
80088                 if (msec) { reveal(null, null, { duration: 0 }); }
80089                 context.map().centerZoomEase(house, 19, msec);
80090
80091                 timeout(function() {
80092                     var tooltip = reveal('button.add-area',
80093                         helpString('intro.buildings.add_building'));
80094
80095                     tooltip.selectAll('.popover-inner')
80096                         .insert('svg', 'span')
80097                         .attr('class', 'tooltip-illustration')
80098                         .append('use')
80099                         .attr('xlink:href', '#iD-graphic-buildings');
80100
80101                     context.on('enter.intro', function(mode) {
80102                         if (mode.id !== 'add-area') { return; }
80103                         continueTo(startHouse);
80104                     });
80105                 }, msec + 100);
80106
80107                 function continueTo(nextStep) {
80108                     context.on('enter.intro', null);
80109                     nextStep();
80110                 }
80111             }
80112
80113
80114             function startHouse() {
80115                 if (context.mode().id !== 'add-area') {
80116                     return continueTo(addHouse);
80117                 }
80118
80119                 _houseID = null;
80120                 context.map().zoomEase(20, 500);
80121
80122                 timeout(function() {
80123                     var startString = helpString('intro.buildings.start_building') +
80124                         helpString('intro.buildings.building_corner_' + (context.lastPointerType() === 'mouse' ? 'click' : 'tap'));
80125                     revealHouse(house, startString);
80126
80127                     context.map().on('move.intro drawn.intro', function() {
80128                         revealHouse(house, startString, { duration: 0 });
80129                     });
80130
80131                     context.on('enter.intro', function(mode) {
80132                         if (mode.id !== 'draw-area') { return chapter.restart(); }
80133                         continueTo(continueHouse);
80134                     });
80135
80136                 }, 550);  // after easing
80137
80138                 function continueTo(nextStep) {
80139                     context.map().on('move.intro drawn.intro', null);
80140                     context.on('enter.intro', null);
80141                     nextStep();
80142                 }
80143             }
80144
80145
80146             function continueHouse() {
80147                 if (context.mode().id !== 'draw-area') {
80148                     return continueTo(addHouse);
80149                 }
80150
80151                 _houseID = null;
80152
80153                 var continueString = helpString('intro.buildings.continue_building') + '{br}' +
80154                     helpString('intro.areas.finish_area_' + (context.lastPointerType() === 'mouse' ? 'click' : 'tap')) +
80155                     helpString('intro.buildings.finish_building');
80156
80157                 revealHouse(house, continueString);
80158
80159                 context.map().on('move.intro drawn.intro', function() {
80160                     revealHouse(house, continueString, { duration: 0 });
80161                 });
80162
80163                 context.on('enter.intro', function(mode) {
80164                     if (mode.id === 'draw-area') {
80165                         return;
80166                     } else if (mode.id === 'select') {
80167                         var graph = context.graph();
80168                         var way = context.entity(context.selectedIDs()[0]);
80169                         var nodes = graph.childNodes(way);
80170                         var points = utilArrayUniq(nodes)
80171                             .map(function(n) { return context.projection(n.loc); });
80172
80173                         if (isMostlySquare(points)) {
80174                             _houseID = way.id;
80175                             return continueTo(chooseCategoryBuilding);
80176                         } else {
80177                             return continueTo(retryHouse);
80178                         }
80179
80180                     } else {
80181                         return chapter.restart();
80182                     }
80183                 });
80184
80185                 function continueTo(nextStep) {
80186                     context.map().on('move.intro drawn.intro', null);
80187                     context.on('enter.intro', null);
80188                     nextStep();
80189                 }
80190             }
80191
80192
80193             function retryHouse() {
80194                 var onClick = function() { continueTo(addHouse); };
80195
80196                 revealHouse(house, helpString('intro.buildings.retry_building'),
80197                     { buttonText: _t('intro.ok'), buttonCallback: onClick }
80198                 );
80199
80200                 context.map().on('move.intro drawn.intro', function() {
80201                     revealHouse(house, helpString('intro.buildings.retry_building'),
80202                         { duration: 0, buttonText: _t('intro.ok'), buttonCallback: onClick }
80203                     );
80204                 });
80205
80206                 function continueTo(nextStep) {
80207                     context.map().on('move.intro drawn.intro', null);
80208                     nextStep();
80209                 }
80210             }
80211
80212
80213             function chooseCategoryBuilding() {
80214                 if (!_houseID || !context.hasEntity(_houseID)) {
80215                     return addHouse();
80216                 }
80217                 var ids = context.selectedIDs();
80218                 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _houseID) {
80219                     context.enter(modeSelect(context, [_houseID]));
80220                 }
80221
80222                 // disallow scrolling
80223                 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
80224
80225                 timeout(function() {
80226                     // reset pane, in case user somehow happened to change it..
80227                     context.container().select('.inspector-wrap .panewrap').style('right', '-100%');
80228
80229                     var button = context.container().select('.preset-category-building .preset-list-button');
80230
80231                     reveal(button.node(),
80232                         helpString('intro.buildings.choose_category_building', { category: buildingCatetory.name() })
80233                     );
80234
80235                     button.on('click.intro', function() {
80236                         button.on('click.intro', null);
80237                         continueTo(choosePresetHouse);
80238                     });
80239
80240                 }, 400);  // after preset list pane visible..
80241
80242
80243                 context.on('enter.intro', function(mode) {
80244                     if (!_houseID || !context.hasEntity(_houseID)) {
80245                         return continueTo(addHouse);
80246                     }
80247                     var ids = context.selectedIDs();
80248                     if (mode.id !== 'select' || !ids.length || ids[0] !== _houseID) {
80249                         return continueTo(chooseCategoryBuilding);
80250                     }
80251                 });
80252
80253                 function continueTo(nextStep) {
80254                     context.container().select('.inspector-wrap').on('wheel.intro', null);
80255                     context.container().select('.preset-list-button').on('click.intro', null);
80256                     context.on('enter.intro', null);
80257                     nextStep();
80258                 }
80259             }
80260
80261
80262             function choosePresetHouse() {
80263                 if (!_houseID || !context.hasEntity(_houseID)) {
80264                     return addHouse();
80265                 }
80266                 var ids = context.selectedIDs();
80267                 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _houseID) {
80268                     context.enter(modeSelect(context, [_houseID]));
80269                 }
80270
80271                 // disallow scrolling
80272                 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
80273
80274                 timeout(function() {
80275                     // reset pane, in case user somehow happened to change it..
80276                     context.container().select('.inspector-wrap .panewrap').style('right', '-100%');
80277
80278                     var button = context.container().select('.preset-building-house .preset-list-button');
80279
80280                     reveal(button.node(),
80281                         helpString('intro.buildings.choose_preset_house', { preset: housePreset.name() }),
80282                         { duration: 300 }
80283                     );
80284
80285                     button.on('click.intro', function() {
80286                         button.on('click.intro', null);
80287                         continueTo(closeEditorHouse);
80288                     });
80289
80290                 }, 400);  // after preset list pane visible..
80291
80292                 context.on('enter.intro', function(mode) {
80293                     if (!_houseID || !context.hasEntity(_houseID)) {
80294                         return continueTo(addHouse);
80295                     }
80296                     var ids = context.selectedIDs();
80297                     if (mode.id !== 'select' || !ids.length || ids[0] !== _houseID) {
80298                         return continueTo(chooseCategoryBuilding);
80299                     }
80300                 });
80301
80302                 function continueTo(nextStep) {
80303                     context.container().select('.inspector-wrap').on('wheel.intro', null);
80304                     context.container().select('.preset-list-button').on('click.intro', null);
80305                     context.on('enter.intro', null);
80306                     nextStep();
80307                 }
80308             }
80309
80310
80311             function closeEditorHouse() {
80312                 if (!_houseID || !context.hasEntity(_houseID)) {
80313                     return addHouse();
80314                 }
80315                 var ids = context.selectedIDs();
80316                 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _houseID) {
80317                     context.enter(modeSelect(context, [_houseID]));
80318                 }
80319
80320                 context.history().checkpoint('hasHouse');
80321
80322                 context.on('exit.intro', function() {
80323                     continueTo(rightClickHouse);
80324                 });
80325
80326                 timeout(function() {
80327                     reveal('.entity-editor-pane',
80328                         helpString('intro.buildings.close', { button: icon('#iD-icon-close', 'pre-text') })
80329                     );
80330                 }, 500);
80331
80332                 function continueTo(nextStep) {
80333                     context.on('exit.intro', null);
80334                     nextStep();
80335                 }
80336             }
80337
80338
80339             function rightClickHouse() {
80340                 if (!_houseID) { return chapter.restart(); }
80341
80342                 context.enter(modeBrowse(context));
80343                 context.history().reset('hasHouse');
80344                 var zoom = context.map().zoom();
80345                 if (zoom < 20) {
80346                     zoom = 20;
80347                 }
80348                 context.map().centerZoomEase(house, zoom, 500);
80349
80350                 context.on('enter.intro', function(mode) {
80351                     if (mode.id !== 'select') { return; }
80352                     var ids = context.selectedIDs();
80353                     if (ids.length !== 1 || ids[0] !== _houseID) { return; }
80354
80355                     timeout(function() {
80356                         var node = selectMenuItem(context, 'orthogonalize').node();
80357                         if (!node) { return; }
80358                         continueTo(clickSquare);
80359                     }, 50);  // after menu visible
80360                 });
80361
80362                 context.map().on('move.intro drawn.intro', function() {
80363                     var rightclickString = helpString('intro.buildings.' + (context.lastPointerType() === 'mouse' ? 'rightclick_building' : 'edit_menu_building_touch'));
80364                     revealHouse(house, rightclickString, { duration: 0 });
80365                 });
80366
80367                 context.history().on('change.intro', function() {
80368                     continueTo(rightClickHouse);
80369                 });
80370
80371                 function continueTo(nextStep) {
80372                     context.on('enter.intro', null);
80373                     context.map().on('move.intro drawn.intro', null);
80374                     context.history().on('change.intro', null);
80375                     nextStep();
80376                 }
80377             }
80378
80379
80380             function clickSquare() {
80381                 if (!_houseID) { return chapter.restart(); }
80382                 var entity = context.hasEntity(_houseID);
80383                 if (!entity) { return continueTo(rightClickHouse); }
80384
80385                 var node = selectMenuItem(context, 'orthogonalize').node();
80386                 if (!node) { return continueTo(rightClickHouse); }
80387
80388                 var wasChanged = false;
80389
80390                 reveal('.edit-menu',
80391                     helpString('intro.buildings.square_building'),
80392                     { padding: 50 }
80393                 );
80394
80395                 context.on('enter.intro', function(mode) {
80396                     if (mode.id === 'browse') {
80397                         continueTo(rightClickHouse);
80398                     } else if (mode.id === 'move' || mode.id === 'rotate') {
80399                         continueTo(retryClickSquare);
80400                     }
80401                 });
80402
80403                 context.map().on('move.intro', function() {
80404                     var node = selectMenuItem(context, 'orthogonalize').node();
80405                     if (!wasChanged && !node) { return continueTo(rightClickHouse); }
80406
80407                     reveal('.edit-menu',
80408                         helpString('intro.buildings.square_building'),
80409                         { duration: 0, padding: 50 }
80410                     );
80411                 });
80412
80413                 context.history().on('change.intro', function() {
80414                     wasChanged = true;
80415                     context.history().on('change.intro', null);
80416
80417                     // Something changed.  Wait for transition to complete and check undo annotation.
80418                     timeout(function() {
80419                         if (context.history().undoAnnotation() === _t('operations.orthogonalize.annotation.feature.single')) {
80420                             continueTo(doneSquare);
80421                         } else {
80422                             continueTo(retryClickSquare);
80423                         }
80424                     }, 500);  // after transitioned actions
80425                 });
80426
80427                 function continueTo(nextStep) {
80428                     context.on('enter.intro', null);
80429                     context.map().on('move.intro', null);
80430                     context.history().on('change.intro', null);
80431                     nextStep();
80432                 }
80433             }
80434
80435
80436             function retryClickSquare() {
80437                 context.enter(modeBrowse(context));
80438
80439                 revealHouse(house, helpString('intro.buildings.retry_square'), {
80440                     buttonText: _t('intro.ok'),
80441                     buttonCallback: function() { continueTo(rightClickHouse); }
80442                 });
80443
80444                 function continueTo(nextStep) {
80445                     nextStep();
80446                 }
80447             }
80448
80449
80450             function doneSquare() {
80451                 context.history().checkpoint('doneSquare');
80452
80453                 revealHouse(house, helpString('intro.buildings.done_square'), {
80454                     buttonText: _t('intro.ok'),
80455                     buttonCallback: function() { continueTo(addTank); }
80456                 });
80457
80458                 function continueTo(nextStep) {
80459                     nextStep();
80460                 }
80461             }
80462
80463
80464             function addTank() {
80465                 context.enter(modeBrowse(context));
80466                 context.history().reset('doneSquare');
80467                 _tankID = null;
80468
80469                 var msec = transitionTime(tank, context.map().center());
80470                 if (msec) { reveal(null, null, { duration: 0 }); }
80471                 context.map().centerZoomEase(tank, 19.5, msec);
80472
80473                 timeout(function() {
80474                     reveal('button.add-area',
80475                         helpString('intro.buildings.add_tank')
80476                     );
80477
80478                     context.on('enter.intro', function(mode) {
80479                         if (mode.id !== 'add-area') { return; }
80480                         continueTo(startTank);
80481                     });
80482                 }, msec + 100);
80483
80484                 function continueTo(nextStep) {
80485                     context.on('enter.intro', null);
80486                     nextStep();
80487                 }
80488             }
80489
80490
80491             function startTank() {
80492                 if (context.mode().id !== 'add-area') {
80493                     return continueTo(addTank);
80494                 }
80495
80496                 _tankID = null;
80497
80498                 timeout(function() {
80499                     var startString = helpString('intro.buildings.start_tank') +
80500                         helpString('intro.buildings.tank_edge_' + (context.lastPointerType() === 'mouse' ? 'click' : 'tap'));
80501                     revealTank(tank, startString);
80502
80503                     context.map().on('move.intro drawn.intro', function() {
80504                         revealTank(tank, startString, { duration: 0 });
80505                     });
80506
80507                     context.on('enter.intro', function(mode) {
80508                         if (mode.id !== 'draw-area') { return chapter.restart(); }
80509                         continueTo(continueTank);
80510                     });
80511
80512                 }, 550);  // after easing
80513
80514                 function continueTo(nextStep) {
80515                     context.map().on('move.intro drawn.intro', null);
80516                     context.on('enter.intro', null);
80517                     nextStep();
80518                 }
80519             }
80520
80521
80522             function continueTank() {
80523                 if (context.mode().id !== 'draw-area') {
80524                     return continueTo(addTank);
80525                 }
80526
80527                 _tankID = null;
80528
80529                 var continueString = helpString('intro.buildings.continue_tank') + '{br}' +
80530                     helpString('intro.areas.finish_area_' + (context.lastPointerType() === 'mouse' ? 'click' : 'tap')) +
80531                     helpString('intro.buildings.finish_tank');
80532
80533                 revealTank(tank, continueString);
80534
80535                 context.map().on('move.intro drawn.intro', function() {
80536                     revealTank(tank, continueString, { duration: 0 });
80537                 });
80538
80539                 context.on('enter.intro', function(mode) {
80540                     if (mode.id === 'draw-area') {
80541                         return;
80542                     } else if (mode.id === 'select') {
80543                         _tankID = context.selectedIDs()[0];
80544                         return continueTo(searchPresetTank);
80545                     } else {
80546                         return continueTo(addTank);
80547                     }
80548                 });
80549
80550                 function continueTo(nextStep) {
80551                     context.map().on('move.intro drawn.intro', null);
80552                     context.on('enter.intro', null);
80553                     nextStep();
80554                 }
80555             }
80556
80557
80558             function searchPresetTank() {
80559                 if (!_tankID || !context.hasEntity(_tankID)) {
80560                     return addTank();
80561                 }
80562                 var ids = context.selectedIDs();
80563                 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _tankID) {
80564                     context.enter(modeSelect(context, [_tankID]));
80565                 }
80566
80567                 // disallow scrolling
80568                 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
80569
80570                 timeout(function() {
80571                     // reset pane, in case user somehow happened to change it..
80572                     context.container().select('.inspector-wrap .panewrap').style('right', '-100%');
80573
80574                     context.container().select('.preset-search-input')
80575                         .on('keydown.intro', null)
80576                         .on('keyup.intro', checkPresetSearch);
80577
80578                     reveal('.preset-search-input',
80579                         helpString('intro.buildings.search_tank', { preset: tankPreset.name() })
80580                     );
80581                 }, 400);  // after preset list pane visible..
80582
80583                 context.on('enter.intro', function(mode) {
80584                     if (!_tankID || !context.hasEntity(_tankID)) {
80585                         return continueTo(addTank);
80586                     }
80587
80588                     var ids = context.selectedIDs();
80589                     if (mode.id !== 'select' || !ids.length || ids[0] !== _tankID) {
80590                         // keep the user's area selected..
80591                         context.enter(modeSelect(context, [_tankID]));
80592
80593                         // reset pane, in case user somehow happened to change it..
80594                         context.container().select('.inspector-wrap .panewrap').style('right', '-100%');
80595                         // disallow scrolling
80596                         context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
80597
80598                         context.container().select('.preset-search-input')
80599                             .on('keydown.intro', null)
80600                             .on('keyup.intro', checkPresetSearch);
80601
80602                         reveal('.preset-search-input',
80603                             helpString('intro.buildings.search_tank', { preset: tankPreset.name() })
80604                         );
80605
80606                         context.history().on('change.intro', null);
80607                     }
80608                 });
80609
80610                 function checkPresetSearch() {
80611                     var first = context.container().select('.preset-list-item:first-child');
80612
80613                     if (first.classed('preset-man_made-storage_tank')) {
80614                         reveal(first.select('.preset-list-button').node(),
80615                             helpString('intro.buildings.choose_tank', { preset: tankPreset.name() }),
80616                             { duration: 300 }
80617                         );
80618
80619                         context.container().select('.preset-search-input')
80620                             .on('keydown.intro', eventCancel, true)
80621                             .on('keyup.intro', null);
80622
80623                         context.history().on('change.intro', function() {
80624                             continueTo(closeEditorTank);
80625                         });
80626                     }
80627                 }
80628
80629                 function continueTo(nextStep) {
80630                     context.container().select('.inspector-wrap').on('wheel.intro', null);
80631                     context.on('enter.intro', null);
80632                     context.history().on('change.intro', null);
80633                     context.container().select('.preset-search-input').on('keydown.intro keyup.intro', null);
80634                     nextStep();
80635                 }
80636             }
80637
80638
80639             function closeEditorTank() {
80640                 if (!_tankID || !context.hasEntity(_tankID)) {
80641                     return addTank();
80642                 }
80643                 var ids = context.selectedIDs();
80644                 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _tankID) {
80645                     context.enter(modeSelect(context, [_tankID]));
80646                 }
80647
80648                 context.history().checkpoint('hasTank');
80649
80650                 context.on('exit.intro', function() {
80651                     continueTo(rightClickTank);
80652                 });
80653
80654                 timeout(function() {
80655                     reveal('.entity-editor-pane',
80656                         helpString('intro.buildings.close', { button: icon('#iD-icon-close', 'pre-text') })
80657                     );
80658                 }, 500);
80659
80660                 function continueTo(nextStep) {
80661                     context.on('exit.intro', null);
80662                     nextStep();
80663                 }
80664             }
80665
80666
80667             function rightClickTank() {
80668                 if (!_tankID) { return continueTo(addTank); }
80669
80670                 context.enter(modeBrowse(context));
80671                 context.history().reset('hasTank');
80672                 context.map().centerEase(tank, 500);
80673
80674                 timeout(function() {
80675                     context.on('enter.intro', function(mode) {
80676                         if (mode.id !== 'select') { return; }
80677                         var ids = context.selectedIDs();
80678                         if (ids.length !== 1 || ids[0] !== _tankID) { return; }
80679
80680                         timeout(function() {
80681                             var node = selectMenuItem(context, 'circularize').node();
80682                             if (!node) { return; }
80683                             continueTo(clickCircle);
80684                         }, 50);  // after menu visible
80685                     });
80686
80687                     var rightclickString = helpString('intro.buildings.' + (context.lastPointerType() === 'mouse' ? 'rightclick_tank' : 'edit_menu_tank_touch'));
80688
80689                     revealTank(tank, rightclickString);
80690
80691                     context.map().on('move.intro drawn.intro', function() {
80692                         revealTank(tank, rightclickString, { duration: 0 });
80693                     });
80694
80695                     context.history().on('change.intro', function() {
80696                         continueTo(rightClickTank);
80697                     });
80698
80699                 }, 600);
80700
80701                 function continueTo(nextStep) {
80702                     context.on('enter.intro', null);
80703                     context.map().on('move.intro drawn.intro', null);
80704                     context.history().on('change.intro', null);
80705                     nextStep();
80706                 }
80707             }
80708
80709
80710             function clickCircle() {
80711                 if (!_tankID) { return chapter.restart(); }
80712                 var entity = context.hasEntity(_tankID);
80713                 if (!entity) { return continueTo(rightClickTank); }
80714
80715                 var node = selectMenuItem(context, 'circularize').node();
80716                 if (!node) { return continueTo(rightClickTank); }
80717
80718                 var wasChanged = false;
80719
80720                 reveal('.edit-menu',
80721                     helpString('intro.buildings.circle_tank'),
80722                     { padding: 50 }
80723                 );
80724
80725                 context.on('enter.intro', function(mode) {
80726                     if (mode.id === 'browse') {
80727                         continueTo(rightClickTank);
80728                     } else if (mode.id === 'move' || mode.id === 'rotate') {
80729                         continueTo(retryClickCircle);
80730                     }
80731                 });
80732
80733                 context.map().on('move.intro', function() {
80734                     var node = selectMenuItem(context, 'circularize').node();
80735                     if (!wasChanged && !node) { return continueTo(rightClickTank); }
80736
80737                     reveal('.edit-menu',
80738                         helpString('intro.buildings.circle_tank'),
80739                         { duration: 0, padding: 50 }
80740                     );
80741                 });
80742
80743                 context.history().on('change.intro', function() {
80744                     wasChanged = true;
80745                     context.history().on('change.intro', null);
80746
80747                     // Something changed.  Wait for transition to complete and check undo annotation.
80748                     timeout(function() {
80749                         if (context.history().undoAnnotation() === _t('operations.circularize.annotation.single')) {
80750                             continueTo(play);
80751                         } else {
80752                             continueTo(retryClickCircle);
80753                         }
80754                     }, 500);  // after transitioned actions
80755                 });
80756
80757                 function continueTo(nextStep) {
80758                     context.on('enter.intro', null);
80759                     context.map().on('move.intro', null);
80760                     context.history().on('change.intro', null);
80761                     nextStep();
80762                 }
80763             }
80764
80765
80766             function retryClickCircle() {
80767                 context.enter(modeBrowse(context));
80768
80769                 revealTank(tank, helpString('intro.buildings.retry_circle'), {
80770                     buttonText: _t('intro.ok'),
80771                     buttonCallback: function() { continueTo(rightClickTank); }
80772                 });
80773
80774                 function continueTo(nextStep) {
80775                     nextStep();
80776                 }
80777             }
80778
80779
80780             function play() {
80781                 dispatch$1.call('done');
80782                 reveal('.ideditor',
80783                     helpString('intro.buildings.play', { next: _t('intro.startediting.title') }), {
80784                         tooltipBox: '.intro-nav-wrap .chapter-startEditing',
80785                         buttonText: _t('intro.ok'),
80786                         buttonCallback: function() { reveal('.ideditor'); }
80787                     }
80788                 );
80789             }
80790
80791
80792             chapter.enter = function() {
80793                 addHouse();
80794             };
80795
80796
80797             chapter.exit = function() {
80798                 timeouts.forEach(window.clearTimeout);
80799                 context.on('enter.intro exit.intro', null);
80800                 context.map().on('move.intro drawn.intro', null);
80801                 context.history().on('change.intro', null);
80802                 context.container().select('.inspector-wrap').on('wheel.intro', null);
80803                 context.container().select('.preset-search-input').on('keydown.intro keyup.intro', null);
80804                 context.container().select('.more-fields .combobox-input').on('click.intro', null);
80805             };
80806
80807
80808             chapter.restart = function() {
80809                 chapter.exit();
80810                 chapter.enter();
80811             };
80812
80813
80814             return utilRebind(chapter, dispatch$1, 'on');
80815         }
80816
80817         function uiIntroStartEditing(context, reveal) {
80818             var dispatch$1 = dispatch('done', 'startEditing');
80819             var modalSelection = select(null);
80820
80821
80822             var chapter = {
80823                 title: 'intro.startediting.title'
80824             };
80825
80826             function showHelp() {
80827                 reveal('.map-control.help-control',
80828                     helpString('intro.startediting.help'), {
80829                         buttonText: _t('intro.ok'),
80830                         buttonCallback: function() { shortcuts(); }
80831                     }
80832                 );
80833             }
80834
80835             function shortcuts() {
80836                 reveal('.map-control.help-control',
80837                     helpString('intro.startediting.shortcuts'), {
80838                         buttonText: _t('intro.ok'),
80839                         buttonCallback: function() { showSave(); }
80840                     }
80841                 );
80842             }
80843
80844             function showSave() {
80845                 context.container().selectAll('.shaded').remove();  // in case user opened keyboard shortcuts
80846                 reveal('.top-toolbar button.save',
80847                     helpString('intro.startediting.save'), {
80848                         buttonText: _t('intro.ok'),
80849                         buttonCallback: function() { showStart(); }
80850                     }
80851                 );
80852             }
80853
80854             function showStart() {
80855                 context.container().selectAll('.shaded').remove();  // in case user opened keyboard shortcuts
80856
80857                 modalSelection = uiModal(context.container());
80858
80859                 modalSelection.select('.modal')
80860                     .attr('class', 'modal-splash modal');
80861
80862                 modalSelection.selectAll('.close').remove();
80863
80864                 var startbutton = modalSelection.select('.content')
80865                     .attr('class', 'fillL')
80866                     .append('button')
80867                         .attr('class', 'modal-section huge-modal-button')
80868                         .on('click', function() {
80869                             modalSelection.remove();
80870                         });
80871
80872                     startbutton
80873                         .append('svg')
80874                         .attr('class', 'illustration')
80875                         .append('use')
80876                         .attr('xlink:href', '#iD-logo-walkthrough');
80877
80878                     startbutton
80879                         .append('h2')
80880                         .text(_t('intro.startediting.start'));
80881
80882                 dispatch$1.call('startEditing');
80883             }
80884
80885
80886             chapter.enter = function() {
80887                 showHelp();
80888             };
80889
80890
80891             chapter.exit = function() {
80892                 modalSelection.remove();
80893                 context.container().selectAll('.shaded').remove();  // in case user opened keyboard shortcuts
80894             };
80895
80896
80897             return utilRebind(chapter, dispatch$1, 'on');
80898         }
80899
80900         var chapterUi = {
80901           welcome: uiIntroWelcome,
80902           navigation: uiIntroNavigation,
80903           point: uiIntroPoint,
80904           area: uiIntroArea,
80905           line: uiIntroLine,
80906           building: uiIntroBuilding,
80907           startEditing: uiIntroStartEditing
80908         };
80909
80910         var chapterFlow = [
80911           'welcome',
80912           'navigation',
80913           'point',
80914           'area',
80915           'line',
80916           'building',
80917           'startEditing'
80918         ];
80919
80920
80921         function uiIntro(context) {
80922           var INTRO_IMAGERY = 'EsriWorldImageryClarity';
80923           var _introGraph = {};
80924           var _currChapter;
80925
80926
80927           function intro(selection) {
80928             _mainFileFetcher.get('intro_graph')
80929               .then(function (dataIntroGraph) {
80930                 // create entities for intro graph and localize names
80931                 for (var id in dataIntroGraph) {
80932                   if (!_introGraph[id]) {
80933                     _introGraph[id] = osmEntity(localize(dataIntroGraph[id]));
80934                   }
80935                 }
80936                 selection.call(startIntro);
80937               })
80938               .catch(function() { /* ignore */ });
80939           }
80940
80941
80942           function startIntro(selection) {
80943             context.enter(modeBrowse(context));
80944
80945             // Save current map state
80946             var osm = context.connection();
80947             var history = context.history().toJSON();
80948             var hash = window.location.hash;
80949             var center = context.map().center();
80950             var zoom = context.map().zoom();
80951             var background = context.background().baseLayerSource();
80952             var overlays = context.background().overlayLayerSources();
80953             var opacity = context.container().selectAll('.main-map .layer-background').style('opacity');
80954             var caches = osm && osm.caches();
80955             var baseEntities = context.history().graph().base().entities;
80956
80957             // Show sidebar and disable the sidebar resizing button
80958             // (this needs to be before `context.inIntro(true)`)
80959             context.ui().sidebar.expand();
80960             context.container().selectAll('button.sidebar-toggle').classed('disabled', true);
80961
80962             // Block saving
80963             context.inIntro(true);
80964
80965             // Load semi-real data used in intro
80966             if (osm) { osm.toggle(false).reset(); }
80967             context.history().reset();
80968             context.history().merge(Object.values(coreGraph().load(_introGraph).entities));
80969             context.history().checkpoint('initial');
80970
80971             // Setup imagery
80972             var imagery = context.background().findSource(INTRO_IMAGERY);
80973             if (imagery) {
80974               context.background().baseLayerSource(imagery);
80975             } else {
80976               context.background().bing();
80977             }
80978             overlays.forEach(function (d) { return context.background().toggleOverlayLayer(d); });
80979
80980             // Setup data layers (only OSM)
80981             var layers = context.layers();
80982             layers.all().forEach(function (item) {
80983               // if the layer has the function `enabled`
80984               if (typeof item.layer.enabled === 'function') {
80985                 item.layer.enabled(item.id === 'osm');
80986               }
80987             });
80988
80989
80990             context.container().selectAll('.main-map .layer-background').style('opacity', 1);
80991
80992             var curtain = uiCurtain(context.container().node());
80993             selection.call(curtain);
80994
80995             // Store that the user started the walkthrough..
80996             corePreferences('walkthrough_started', 'yes');
80997
80998             // Restore previous walkthrough progress..
80999             var storedProgress = corePreferences('walkthrough_progress') || '';
81000             var progress = storedProgress.split(';').filter(Boolean);
81001
81002             var chapters = chapterFlow.map(function (chapter, i) {
81003               var s = chapterUi[chapter](context, curtain.reveal)
81004                 .on('done', function () {
81005
81006                   buttons
81007                     .filter(function (d) { return d.title === s.title; })
81008                     .classed('finished', true);
81009
81010                   if (i < chapterFlow.length - 1) {
81011                     var next = chapterFlow[i + 1];
81012                     context.container().select(("button.chapter-" + next))
81013                       .classed('next', true);
81014                   }
81015
81016                   // Store walkthrough progress..
81017                   progress.push(chapter);
81018                   corePreferences('walkthrough_progress', utilArrayUniq(progress).join(';'));
81019                 });
81020               return s;
81021             });
81022
81023             chapters[chapters.length - 1].on('startEditing', function () {
81024               // Store walkthrough progress..
81025               progress.push('startEditing');
81026               corePreferences('walkthrough_progress', utilArrayUniq(progress).join(';'));
81027
81028               // Store if walkthrough is completed..
81029               var incomplete = utilArrayDifference(chapterFlow, progress);
81030               if (!incomplete.length) {
81031                 corePreferences('walkthrough_completed', 'yes');
81032               }
81033
81034               curtain.remove();
81035               navwrap.remove();
81036               context.container().selectAll('.main-map .layer-background').style('opacity', opacity);
81037               context.container().selectAll('button.sidebar-toggle').classed('disabled', false);
81038               if (osm) { osm.toggle(true).reset().caches(caches); }
81039               context.history().reset().merge(Object.values(baseEntities));
81040               context.background().baseLayerSource(background);
81041               overlays.forEach(function (d) { return context.background().toggleOverlayLayer(d); });
81042               if (history) { context.history().fromJSON(history, false); }
81043               context.map().centerZoom(center, zoom);
81044               window.location.replace(hash);
81045               context.inIntro(false);
81046             });
81047
81048             var navwrap = selection
81049               .append('div')
81050               .attr('class', 'intro-nav-wrap fillD');
81051
81052             navwrap
81053               .append('svg')
81054               .attr('class', 'intro-nav-wrap-logo')
81055               .append('use')
81056               .attr('xlink:href', '#iD-logo-walkthrough');
81057
81058             var buttonwrap = navwrap
81059               .append('div')
81060               .attr('class', 'joined')
81061               .selectAll('button.chapter');
81062
81063             var buttons = buttonwrap
81064               .data(chapters)
81065               .enter()
81066               .append('button')
81067               .attr('class', function (d, i) { return ("chapter chapter-" + (chapterFlow[i])); })
81068               .on('click', enterChapter);
81069
81070             buttons
81071               .append('span')
81072               .text(function (d) { return _t(d.title); });
81073
81074             buttons
81075               .append('span')
81076               .attr('class', 'status')
81077               .call(svgIcon((_mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-backward' : '#iD-icon-forward'), 'inline'));
81078
81079             enterChapter(chapters[0]);
81080
81081
81082             function enterChapter(newChapter) {
81083               if (_currChapter) { _currChapter.exit(); }
81084               context.enter(modeBrowse(context));
81085
81086               _currChapter = newChapter;
81087               _currChapter.enter();
81088
81089               buttons
81090                 .classed('next', false)
81091                 .classed('active', function (d) { return d.title === _currChapter.title; });
81092             }
81093           }
81094
81095
81096           return intro;
81097         }
81098
81099         function uiIssuesInfo(context) {
81100
81101             var warningsItem = {
81102                 id: 'warnings',
81103                 count: 0,
81104                 iconID: 'iD-icon-alert',
81105                 descriptionID: 'issues.warnings_and_errors'
81106             };
81107
81108             var resolvedItem = {
81109                 id: 'resolved',
81110                 count: 0,
81111                 iconID: 'iD-icon-apply',
81112                 descriptionID: 'issues.user_resolved_issues'
81113             };
81114
81115             function update(selection) {
81116
81117                 var shownItems = [];
81118
81119                 var liveIssues = context.validator().getIssues({
81120                     what: corePreferences('validate-what') || 'edited',
81121                     where: corePreferences('validate-where') || 'all'
81122                 });
81123                 if (liveIssues.length) {
81124                     warningsItem.count = liveIssues.length;
81125                     shownItems.push(warningsItem);
81126                 }
81127
81128                 if (corePreferences('validate-what') === 'all') {
81129                     var resolvedIssues = context.validator().getResolvedIssues();
81130                     if (resolvedIssues.length) {
81131                         resolvedItem.count = resolvedIssues.length;
81132                         shownItems.push(resolvedItem);
81133                     }
81134                 }
81135
81136                 var chips = selection.selectAll('.chip')
81137                     .data(shownItems, function(d) {
81138                         return d.id;
81139                     });
81140
81141                 chips.exit().remove();
81142
81143                 var enter = chips.enter()
81144                     .append('a')
81145                     .attr('class', function(d) {
81146                         return 'chip ' + d.id + '-count';
81147                     })
81148                     .attr('href', '#')
81149                     .attr('tabindex', -1)
81150                     .each(function(d) {
81151
81152                         var chipSelection = select(this);
81153
81154                         var tooltipBehavior = uiTooltip()
81155                             .placement('top')
81156                             .title(_t(d.descriptionID));
81157
81158                         chipSelection
81159                             .call(tooltipBehavior)
81160                             .on('click', function() {
81161                                 event.preventDefault();
81162
81163                                 tooltipBehavior.hide(select(this));
81164                                 // open the Issues pane
81165                                 context.ui().togglePanes(context.container().select('.map-panes .issues-pane'));
81166                             });
81167
81168                         chipSelection.call(svgIcon('#' + d.iconID));
81169
81170                     });
81171
81172                 enter.append('span')
81173                     .attr('class', 'count');
81174
81175                 enter.merge(chips)
81176                     .selectAll('span.count')
81177                     .text(function(d) {
81178                         return d.count.toString();
81179                     });
81180             }
81181
81182
81183             return function(selection) {
81184                 update(selection);
81185
81186                 context.validator().on('validated.infobox', function() {
81187                     update(selection);
81188                 });
81189             };
81190         }
81191
81192         // import { utilGetDimensions } from '../util/dimensions';
81193
81194
81195         function uiMapInMap(context) {
81196
81197             function mapInMap(selection) {
81198                 var backgroundLayer = rendererTileLayer(context);
81199                 var overlayLayers = {};
81200                 var projection = geoRawMercator();
81201                 var dataLayer = svgData(projection, context).showLabels(false);
81202                 var debugLayer = svgDebug(projection, context);
81203                 var zoom = d3_zoom()
81204                     .scaleExtent([geoZoomToScale(0.5), geoZoomToScale(24)])
81205                     .on('start', zoomStarted)
81206                     .on('zoom', zoomed)
81207                     .on('end', zoomEnded);
81208
81209                 var wrap = select(null);
81210                 var tiles = select(null);
81211                 var viewport = select(null);
81212
81213                 var _isTransformed = false;
81214                 var _isHidden = true;
81215                 var _skipEvents = false;
81216                 var _gesture = null;
81217                 var _zDiff = 6;    // by default, minimap renders at (main zoom - 6)
81218                 var _dMini;        // dimensions of minimap
81219                 var _cMini;        // center pixel of minimap
81220                 var _tStart;       // transform at start of gesture
81221                 var _tCurr;        // transform at most recent event
81222                 var _timeoutID;
81223
81224
81225                 function zoomStarted() {
81226                     if (_skipEvents) { return; }
81227                     _tStart = _tCurr = projection.transform();
81228                     _gesture = null;
81229                 }
81230
81231
81232                 function zoomed() {
81233                     if (_skipEvents) { return; }
81234
81235                     var x = event.transform.x;
81236                     var y = event.transform.y;
81237                     var k = event.transform.k;
81238                     var isZooming = (k !== _tStart.k);
81239                     var isPanning = (x !== _tStart.x || y !== _tStart.y);
81240
81241                     if (!isZooming && !isPanning) {
81242                         return;  // no change
81243                     }
81244
81245                     // lock in either zooming or panning, don't allow both in minimap.
81246                     if (!_gesture) {
81247                         _gesture = isZooming ? 'zoom' : 'pan';
81248                     }
81249
81250                     var tMini = projection.transform();
81251                     var tX, tY, scale;
81252
81253                     if (_gesture === 'zoom') {
81254                         scale = k / tMini.k;
81255                         tX = (_cMini[0] / scale - _cMini[0]) * scale;
81256                         tY = (_cMini[1] / scale - _cMini[1]) * scale;
81257                     } else {
81258                         k = tMini.k;
81259                         scale = 1;
81260                         tX = x - tMini.x;
81261                         tY = y - tMini.y;
81262                     }
81263
81264                     utilSetTransform(tiles, tX, tY, scale);
81265                     utilSetTransform(viewport, 0, 0, scale);
81266                     _isTransformed = true;
81267                     _tCurr = identity$2.translate(x, y).scale(k);
81268
81269                     var zMain = geoScaleToZoom(context.projection.scale());
81270                     var zMini = geoScaleToZoom(k);
81271
81272                     _zDiff = zMain - zMini;
81273
81274                     queueRedraw();
81275                 }
81276
81277
81278                 function zoomEnded() {
81279                     if (_skipEvents) { return; }
81280                     if (_gesture !== 'pan') { return; }
81281
81282                     updateProjection();
81283                     _gesture = null;
81284                     context.map().center(projection.invert(_cMini));   // recenter main map..
81285                 }
81286
81287
81288                 function updateProjection() {
81289                     var loc = context.map().center();
81290                     var tMain = context.projection.transform();
81291                     var zMain = geoScaleToZoom(tMain.k);
81292                     var zMini = Math.max(zMain - _zDiff, 0.5);
81293                     var kMini = geoZoomToScale(zMini);
81294
81295                     projection
81296                         .translate([tMain.x, tMain.y])
81297                         .scale(kMini);
81298
81299                     var point = projection(loc);
81300                     var mouse = (_gesture === 'pan') ? geoVecSubtract([_tCurr.x, _tCurr.y], [_tStart.x, _tStart.y]) : [0, 0];
81301                     var xMini = _cMini[0] - point[0] + tMain.x + mouse[0];
81302                     var yMini = _cMini[1] - point[1] + tMain.y + mouse[1];
81303
81304                     projection
81305                         .translate([xMini, yMini])
81306                         .clipExtent([[0, 0], _dMini]);
81307
81308                     _tCurr = projection.transform();
81309
81310                     if (_isTransformed) {
81311                         utilSetTransform(tiles, 0, 0);
81312                         utilSetTransform(viewport, 0, 0);
81313                         _isTransformed = false;
81314                     }
81315
81316                     zoom
81317                         .scaleExtent([geoZoomToScale(0.5), geoZoomToScale(zMain - 3)]);
81318
81319                     _skipEvents = true;
81320                     wrap.call(zoom.transform, _tCurr);
81321                     _skipEvents = false;
81322                 }
81323
81324
81325                 function redraw() {
81326                     clearTimeout(_timeoutID);
81327                     if (_isHidden) { return; }
81328
81329                     updateProjection();
81330                     var zMini = geoScaleToZoom(projection.scale());
81331
81332                     // setup tile container
81333                     tiles = wrap
81334                         .selectAll('.map-in-map-tiles')
81335                         .data([0]);
81336
81337                     tiles = tiles.enter()
81338                         .append('div')
81339                         .attr('class', 'map-in-map-tiles')
81340                         .merge(tiles);
81341
81342                     // redraw background
81343                     backgroundLayer
81344                         .source(context.background().baseLayerSource())
81345                         .projection(projection)
81346                         .dimensions(_dMini);
81347
81348                     var background = tiles
81349                         .selectAll('.map-in-map-background')
81350                         .data([0]);
81351
81352                     background.enter()
81353                         .append('div')
81354                         .attr('class', 'map-in-map-background')
81355                         .merge(background)
81356                         .call(backgroundLayer);
81357
81358
81359                     // redraw overlay
81360                     var overlaySources = context.background().overlayLayerSources();
81361                     var activeOverlayLayers = [];
81362                     for (var i = 0; i < overlaySources.length; i++) {
81363                         if (overlaySources[i].validZoom(zMini)) {
81364                             if (!overlayLayers[i]) { overlayLayers[i] = rendererTileLayer(context); }
81365                             activeOverlayLayers.push(overlayLayers[i]
81366                                 .source(overlaySources[i])
81367                                 .projection(projection)
81368                                 .dimensions(_dMini));
81369                         }
81370                     }
81371
81372                     var overlay = tiles
81373                         .selectAll('.map-in-map-overlay')
81374                         .data([0]);
81375
81376                     overlay = overlay.enter()
81377                         .append('div')
81378                         .attr('class', 'map-in-map-overlay')
81379                         .merge(overlay);
81380
81381
81382                     var overlays = overlay
81383                         .selectAll('div')
81384                         .data(activeOverlayLayers, function(d) { return d.source().name(); });
81385
81386                     overlays.exit()
81387                         .remove();
81388
81389                     overlays = overlays.enter()
81390                         .append('div')
81391                         .merge(overlays)
81392                         .each(function(layer) { select(this).call(layer); });
81393
81394
81395                     var dataLayers = tiles
81396                         .selectAll('.map-in-map-data')
81397                         .data([0]);
81398
81399                     dataLayers.exit()
81400                         .remove();
81401
81402                     dataLayers = dataLayers.enter()
81403                         .append('svg')
81404                         .attr('class', 'map-in-map-data')
81405                         .merge(dataLayers)
81406                         .call(dataLayer)
81407                         .call(debugLayer);
81408
81409
81410                     // redraw viewport bounding box
81411                     if (_gesture !== 'pan') {
81412                         var getPath = d3_geoPath(projection);
81413                         var bbox = { type: 'Polygon', coordinates: [context.map().extent().polygon()] };
81414
81415                         viewport = wrap.selectAll('.map-in-map-viewport')
81416                             .data([0]);
81417
81418                         viewport = viewport.enter()
81419                             .append('svg')
81420                             .attr('class', 'map-in-map-viewport')
81421                             .merge(viewport);
81422
81423
81424                         var path = viewport.selectAll('.map-in-map-bbox')
81425                             .data([bbox]);
81426
81427                         path.enter()
81428                             .append('path')
81429                             .attr('class', 'map-in-map-bbox')
81430                             .merge(path)
81431                             .attr('d', getPath)
81432                             .classed('thick', function(d) { return getPath.area(d) < 30; });
81433                     }
81434                 }
81435
81436
81437                 function queueRedraw() {
81438                     clearTimeout(_timeoutID);
81439                     _timeoutID = setTimeout(function() { redraw(); }, 750);
81440                 }
81441
81442
81443                 function toggle() {
81444                     if (event) { event.preventDefault(); }
81445
81446                     _isHidden = !_isHidden;
81447
81448                     context.container().select('.minimap-toggle-item')
81449                         .classed('active', !_isHidden)
81450                         .select('input')
81451                         .property('checked', !_isHidden);
81452
81453                     if (_isHidden) {
81454                         wrap
81455                             .style('display', 'block')
81456                             .style('opacity', '1')
81457                             .transition()
81458                             .duration(200)
81459                             .style('opacity', '0')
81460                             .on('end', function() {
81461                                 selection.selectAll('.map-in-map')
81462                                     .style('display', 'none');
81463                             });
81464                     } else {
81465                         wrap
81466                             .style('display', 'block')
81467                             .style('opacity', '0')
81468                             .transition()
81469                             .duration(200)
81470                             .style('opacity', '1')
81471                             .on('end', function() {
81472                                 redraw();
81473                             });
81474                     }
81475                 }
81476
81477
81478                 uiMapInMap.toggle = toggle;
81479
81480                 wrap = selection.selectAll('.map-in-map')
81481                     .data([0]);
81482
81483                 wrap = wrap.enter()
81484                     .append('div')
81485                     .attr('class', 'map-in-map')
81486                     .style('display', (_isHidden ? 'none' : 'block'))
81487                     .call(zoom)
81488                     .on('dblclick.zoom', null)
81489                     .merge(wrap);
81490
81491                 // reflow warning: Hardcode dimensions - currently can't resize it anyway..
81492                 _dMini = [200,150]; //utilGetDimensions(wrap);
81493                 _cMini = geoVecScale(_dMini, 0.5);
81494
81495                 context.map()
81496                     .on('drawn.map-in-map', function(drawn) {
81497                         if (drawn.full === true) {
81498                             redraw();
81499                         }
81500                     });
81501
81502                 redraw();
81503
81504                 context.keybinding()
81505                     .on(_t('background.minimap.key'), toggle);
81506             }
81507
81508             return mapInMap;
81509         }
81510
81511         function uiNotice(context) {
81512
81513             return function(selection) {
81514                 var div = selection
81515                     .append('div')
81516                     .attr('class', 'notice');
81517
81518                 var button = div
81519                     .append('button')
81520                     .attr('class', 'zoom-to notice fillD')
81521                     .on('click', function() {
81522                         context.map().zoomEase(context.minEditableZoom());
81523                     })
81524                     .on('wheel', function() {   // let wheel events pass through #4482
81525                         var e2 = new WheelEvent(event.type, event);
81526                         context.surface().node().dispatchEvent(e2);
81527                     });
81528
81529                 button
81530                     .call(svgIcon('#iD-icon-plus', 'pre-text'))
81531                     .append('span')
81532                     .attr('class', 'label')
81533                     .text(_t('zoom_in_edit'));
81534
81535
81536                 function disableTooHigh() {
81537                     var canEdit = context.map().zoom() >= context.minEditableZoom();
81538                     div.style('display', canEdit ? 'none' : 'block');
81539                 }
81540
81541                 context.map()
81542                     .on('move.notice', debounce(disableTooHigh, 500));
81543
81544                 disableTooHigh();
81545             };
81546         }
81547
81548         function uiPhotoviewer(context) {
81549
81550             var dispatch$1 = dispatch('resize');
81551
81552             var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
81553
81554             function photoviewer(selection) {
81555                 selection
81556                     .append('button')
81557                     .attr('class', 'thumb-hide')
81558                     .on('click', function () {
81559                         if (services.streetside) { services.streetside.hideViewer(context); }
81560                         if (services.mapillary) { services.mapillary.hideViewer(context); }
81561                         if (services.openstreetcam) { services.openstreetcam.hideViewer(context); }
81562                     })
81563                     .append('div')
81564                     .call(svgIcon('#iD-icon-close'));
81565
81566                 function preventDefault() {
81567                     event.preventDefault();
81568                 }
81569
81570                 selection
81571                     .append('button')
81572                     .attr('class', 'resize-handle-xy')
81573                     .on('touchstart touchdown touchend', preventDefault)
81574                     .on(
81575                         _pointerPrefix + 'down',
81576                         buildResizeListener(selection, 'resize', dispatch$1, { resizeOnX: true, resizeOnY: true })
81577                     );
81578
81579                 selection
81580                     .append('button')
81581                     .attr('class', 'resize-handle-x')
81582                     .on('touchstart touchdown touchend', preventDefault)
81583                     .on(
81584                         _pointerPrefix + 'down',
81585                         buildResizeListener(selection, 'resize', dispatch$1, { resizeOnX: true })
81586                     );
81587
81588                 selection
81589                     .append('button')
81590                     .attr('class', 'resize-handle-y')
81591                     .on('touchstart touchdown touchend', preventDefault)
81592                     .on(
81593                         _pointerPrefix + 'down',
81594                         buildResizeListener(selection, 'resize', dispatch$1, { resizeOnY: true })
81595                     );
81596
81597                 services.streetside.loadViewer(context);
81598                 services.mapillary.loadViewer(context);
81599                 services.openstreetcam.loadViewer(context);
81600
81601                 function buildResizeListener(target, eventName, dispatch, options) {
81602
81603                     var resizeOnX = !!options.resizeOnX;
81604                     var resizeOnY = !!options.resizeOnY;
81605                     var minHeight = options.minHeight || 240;
81606                     var minWidth = options.minWidth || 320;
81607                     var pointerId;
81608                     var startX;
81609                     var startY;
81610                     var startWidth;
81611                     var startHeight;
81612
81613                     function startResize() {
81614                         if (pointerId !== (event.pointerId || 'mouse')) { return; }
81615
81616                         event.preventDefault();
81617                         event.stopPropagation();
81618
81619                         var mapSize = context.map().dimensions();
81620
81621                         if (resizeOnX) {
81622                             var maxWidth = mapSize[0];
81623                             var newWidth = clamp((startWidth + event.clientX - startX), minWidth, maxWidth);
81624                             target.style('width', newWidth + 'px');
81625                         }
81626
81627                         if (resizeOnY) {
81628                             var maxHeight = mapSize[1] - 90;  // preserve space at top/bottom of map
81629                             var newHeight = clamp((startHeight + startY - event.clientY), minHeight, maxHeight);
81630                             target.style('height', newHeight + 'px');
81631                         }
81632
81633                         dispatch.call(eventName, target, utilGetDimensions(target, true));
81634                     }
81635
81636                     function clamp(num, min, max) {
81637                         return Math.max(min, Math.min(num, max));
81638                     }
81639
81640                     function stopResize() {
81641                         if (pointerId !== (event.pointerId || 'mouse')) { return; }
81642
81643                         event.preventDefault();
81644                         event.stopPropagation();
81645
81646                         // remove all the listeners we added
81647                         select(window)
81648                             .on('.' + eventName, null);
81649                     }
81650
81651                     return function initResize() {
81652                         event.preventDefault();
81653                         event.stopPropagation();
81654
81655                         pointerId = event.pointerId || 'mouse';
81656
81657                         startX = event.clientX;
81658                         startY = event.clientY;
81659                         var targetRect = target.node().getBoundingClientRect();
81660                         startWidth = targetRect.width;
81661                         startHeight = targetRect.height;
81662
81663                         select(window)
81664                             .on(_pointerPrefix + 'move.' + eventName, startResize, false)
81665                             .on(_pointerPrefix + 'up.' + eventName, stopResize, false);
81666
81667                         if (_pointerPrefix === 'pointer') {
81668                             select(window)
81669                                 .on('pointercancel.' + eventName, stopResize, false);
81670                         }
81671                     };
81672                 }
81673             }
81674
81675             photoviewer.onMapResize = function() {
81676                 var photoviewer = context.container().select('.photoviewer');
81677                 var content = context.container().select('.main-content');
81678                 var mapDimensions = utilGetDimensions(content, true);
81679                 // shrink photo viewer if it is too big
81680                 // (-90 preserves space at top and bottom of map used by menus)
81681                 var photoDimensions = utilGetDimensions(photoviewer, true);
81682                 if (photoDimensions[0] > mapDimensions[0] || photoDimensions[1] > (mapDimensions[1] - 90)) {
81683                     var setPhotoDimensions = [
81684                         Math.min(photoDimensions[0], mapDimensions[0]),
81685                         Math.min(photoDimensions[1], mapDimensions[1] - 90) ];
81686
81687                     photoviewer
81688                         .style('width', setPhotoDimensions[0] + 'px')
81689                         .style('height', setPhotoDimensions[1] + 'px');
81690
81691                     dispatch$1.call('resize', photoviewer, setPhotoDimensions);
81692                 }
81693             };
81694
81695             return utilRebind(photoviewer, dispatch$1, 'on');
81696         }
81697
81698         function uiRestore(context) {
81699           return function(selection) {
81700             if (!context.history().hasRestorableChanges()) { return; }
81701
81702             var modalSelection = uiModal(selection, true);
81703
81704             modalSelection.select('.modal')
81705               .attr('class', 'modal fillL');
81706
81707             var introModal = modalSelection.select('.content');
81708
81709             introModal
81710               .append('div')
81711               .attr('class', 'modal-section')
81712               .append('h3')
81713               .text(_t('restore.heading'));
81714
81715             introModal
81716               .append('div')
81717               .attr('class','modal-section')
81718               .append('p')
81719               .text(_t('restore.description'));
81720
81721             var buttonWrap = introModal
81722               .append('div')
81723               .attr('class', 'modal-actions');
81724
81725             var restore = buttonWrap
81726               .append('button')
81727               .attr('class', 'restore')
81728               .on('click', function () {
81729                 context.history().restore();
81730                 modalSelection.remove();
81731               });
81732
81733             restore
81734               .append('svg')
81735               .attr('class', 'logo logo-restore')
81736               .append('use')
81737               .attr('xlink:href', '#iD-logo-restore');
81738
81739             restore
81740               .append('div')
81741               .text(_t('restore.restore'));
81742
81743             var reset = buttonWrap
81744               .append('button')
81745               .attr('class', 'reset')
81746               .on('click', function () {
81747                 context.history().clearSaved();
81748                 modalSelection.remove();
81749               });
81750
81751             reset
81752               .append('svg')
81753               .attr('class', 'logo logo-reset')
81754               .append('use')
81755               .attr('xlink:href', '#iD-logo-reset');
81756
81757             reset
81758               .append('div')
81759               .text(_t('restore.reset'));
81760
81761             restore.node().focus();
81762           };
81763         }
81764
81765         function uiScale(context) {
81766             var projection = context.projection,
81767                 isImperial = !_mainLocalizer.usesMetric(),
81768                 maxLength = 180,
81769                 tickHeight = 8;
81770
81771
81772             function scaleDefs(loc1, loc2) {
81773                 var lat = (loc2[1] + loc1[1]) / 2,
81774                     conversion = (isImperial ? 3.28084 : 1),
81775                     dist = geoLonToMeters(loc2[0] - loc1[0], lat) * conversion,
81776                     scale = { dist: 0, px: 0, text: '' },
81777                     buckets, i, val, dLon;
81778
81779                 if (isImperial) {
81780                     buckets = [5280000, 528000, 52800, 5280, 500, 50, 5, 1];
81781                 } else {
81782                     buckets = [5000000, 500000, 50000, 5000, 500, 50, 5, 1];
81783                 }
81784
81785                 // determine a user-friendly endpoint for the scale
81786                 for (i = 0; i < buckets.length; i++) {
81787                     val = buckets[i];
81788                     if (dist >= val) {
81789                         scale.dist = Math.floor(dist / val) * val;
81790                         break;
81791                     } else {
81792                         scale.dist = +dist.toFixed(2);
81793                     }
81794                 }
81795
81796                 dLon = geoMetersToLon(scale.dist / conversion, lat);
81797                 scale.px = Math.round(projection([loc1[0] + dLon, loc1[1]])[0]);
81798
81799                 scale.text = displayLength(scale.dist / conversion, isImperial);
81800
81801                 return scale;
81802             }
81803
81804
81805             function update(selection) {
81806                 // choose loc1, loc2 along bottom of viewport (near where the scale will be drawn)
81807                 var dims = context.map().dimensions(),
81808                     loc1 = projection.invert([0, dims[1]]),
81809                     loc2 = projection.invert([maxLength, dims[1]]),
81810                     scale = scaleDefs(loc1, loc2);
81811
81812                 selection.select('.scale-path')
81813                     .attr('d', 'M0.5,0.5v' + tickHeight + 'h' + scale.px + 'v-' + tickHeight);
81814
81815                 selection.select('.scale-textgroup')
81816                     .attr('transform', 'translate(' + (scale.px + 8) + ',' + tickHeight + ')');
81817
81818                 selection.select('.scale-text')
81819                     .text(scale.text);
81820             }
81821
81822
81823             return function(selection) {
81824                 function switchUnits() {
81825                     isImperial = !isImperial;
81826                     selection.call(update);
81827                 }
81828
81829                 var scalegroup = selection.append('svg')
81830                     .attr('class', 'scale')
81831                     .on('click', switchUnits)
81832                     .append('g')
81833                     .attr('transform', 'translate(10,11)');
81834
81835                 scalegroup
81836                     .append('path')
81837                     .attr('class', 'scale-path');
81838
81839                 scalegroup
81840                     .append('g')
81841                     .attr('class', 'scale-textgroup')
81842                     .append('text')
81843                     .attr('class', 'scale-text');
81844
81845                 selection.call(update);
81846
81847                 context.map().on('move.scale', function() {
81848                     update(selection);
81849                 });
81850             };
81851         }
81852
81853         function uiShortcuts(context) {
81854             var detected = utilDetect();
81855             var _activeTab = 0;
81856             var _modalSelection;
81857             var _selection = select(null);
81858
81859
81860             context.keybinding()
81861                 .on([_t('shortcuts.toggle.key'), '?'], function () {
81862                     if (context.container().selectAll('.modal-shortcuts').size()) {  // already showing
81863                         if (_modalSelection) {
81864                             _modalSelection.close();
81865                             _modalSelection = null;
81866                         }
81867                     } else {
81868                         _modalSelection = uiModal(_selection);
81869                         _modalSelection.call(shortcutsModal);
81870                     }
81871                 });
81872
81873
81874             function shortcutsModal(_modalSelection) {
81875                 _modalSelection.select('.modal')
81876                     .classed('modal-shortcuts', true);
81877
81878                 var content = _modalSelection.select('.content');
81879
81880                 content
81881                     .append('div')
81882                     .attr('class', 'modal-section')
81883                     .append('h3')
81884                     .text(_t('shortcuts.title'));
81885
81886                 _mainFileFetcher.get('shortcuts')
81887                     .then(function(data) { content.call(render, data); })
81888                     .catch(function() { /* ignore */ });
81889             }
81890
81891
81892             function render(selection, dataShortcuts) {
81893                 var wrapper = selection
81894                     .selectAll('.wrapper')
81895                     .data([0]);
81896
81897                 var wrapperEnter = wrapper
81898                     .enter()
81899                     .append('div')
81900                     .attr('class', 'wrapper modal-section');
81901
81902                 var tabsBar = wrapperEnter
81903                     .append('div')
81904                     .attr('class', 'tabs-bar');
81905
81906                 var shortcutsList = wrapperEnter
81907                     .append('div')
81908                     .attr('class', 'shortcuts-list');
81909
81910                 wrapper = wrapper.merge(wrapperEnter);
81911
81912                 var tabs = tabsBar
81913                     .selectAll('.tab')
81914                     .data(dataShortcuts);
81915
81916                 var tabsEnter = tabs
81917                     .enter()
81918                     .append('div')
81919                     .attr('class', 'tab')
81920                     .on('click', function (d, i) {
81921                         _activeTab = i;
81922                         render(selection, dataShortcuts);
81923                     });
81924
81925                 tabsEnter
81926                     .append('span')
81927                     .text(function (d) { return _t(d.text); });
81928
81929                 tabs = tabs
81930                     .merge(tabsEnter);
81931
81932                 // Update
81933                 wrapper.selectAll('.tab')
81934                     .classed('active', function (d, i) {
81935                         return i === _activeTab;
81936                     });
81937
81938
81939                 var shortcuts = shortcutsList
81940                     .selectAll('.shortcut-tab')
81941                     .data(dataShortcuts);
81942
81943                 var shortcutsEnter = shortcuts
81944                     .enter()
81945                     .append('div')
81946                     .attr('class', function(d) { return 'shortcut-tab shortcut-tab-' + d.tab; });
81947
81948                 var columnsEnter = shortcutsEnter
81949                     .selectAll('.shortcut-column')
81950                     .data(function (d) { return d.columns; })
81951                     .enter()
81952                     .append('table')
81953                     .attr('class', 'shortcut-column');
81954
81955                 var rowsEnter = columnsEnter
81956                     .selectAll('.shortcut-row')
81957                     .data(function (d) { return d.rows; })
81958                     .enter()
81959                     .append('tr')
81960                     .attr('class', 'shortcut-row');
81961
81962
81963                 var sectionRows = rowsEnter
81964                     .filter(function (d) { return !d.shortcuts; });
81965
81966                 sectionRows
81967                     .append('td');
81968
81969                 sectionRows
81970                     .append('td')
81971                     .attr('class', 'shortcut-section')
81972                     .append('h3')
81973                     .text(function (d) { return _t(d.text); });
81974
81975
81976                 var shortcutRows = rowsEnter
81977                     .filter(function (d) { return d.shortcuts; });
81978
81979                 var shortcutKeys = shortcutRows
81980                     .append('td')
81981                     .attr('class', 'shortcut-keys');
81982
81983                 var modifierKeys = shortcutKeys
81984                     .filter(function (d) { return d.modifiers; });
81985
81986                 modifierKeys
81987                     .selectAll('kbd.modifier')
81988                     .data(function (d) {
81989                         if (detected.os === 'win' && d.text === 'shortcuts.editing.commands.redo') {
81990                             return ['⌘'];
81991                         } else if (detected.os !== 'mac' && d.text === 'shortcuts.browsing.display_options.fullscreen') {
81992                             return [];
81993                         } else {
81994                             return d.modifiers;
81995                         }
81996                     })
81997                     .enter()
81998                     .each(function () {
81999                         var selection = select(this);
82000
82001                         selection
82002                             .append('kbd')
82003                             .attr('class', 'modifier')
82004                             .text(function (d) { return uiCmd.display(d); });
82005
82006                         selection
82007                             .append('span')
82008                             .text('+');
82009                     });
82010
82011
82012                 shortcutKeys
82013                     .selectAll('kbd.shortcut')
82014                     .data(function (d) {
82015                         var arr = d.shortcuts;
82016                         if (detected.os === 'win' && d.text === 'shortcuts.editing.commands.redo') {
82017                             arr = ['Y'];
82018                         } else if (detected.os !== 'mac' && d.text === 'shortcuts.browsing.display_options.fullscreen') {
82019                             arr = ['F11'];
82020                         }
82021
82022                         // replace translations
82023                         arr = arr.map(function(s) {
82024                             return uiCmd.display(s.indexOf('.') !== -1 ? _t(s) : s);
82025                         });
82026
82027                         return utilArrayUniq(arr).map(function(s) {
82028                             return {
82029                                 shortcut: s,
82030                                 separator: d.separator,
82031                                 suffix: d.suffix
82032                             };
82033                         });
82034                     })
82035                     .enter()
82036                     .each(function (d, i, nodes) {
82037                         var selection = select(this);
82038                         var click = d.shortcut.toLowerCase().match(/(.*).click/);
82039
82040                         if (click && click[1]) {   // replace "left_click", "right_click" with mouse icon
82041                             selection
82042                                 .call(svgIcon('#iD-walkthrough-mouse-' + click[1], 'operation'));
82043                         } else if (d.shortcut.toLowerCase() === 'long-press') {
82044                             selection
82045                                 .call(svgIcon('#iD-walkthrough-longpress', 'longpress operation'));
82046                         } else if (d.shortcut.toLowerCase() === 'tap') {
82047                             selection
82048                                 .call(svgIcon('#iD-walkthrough-tap', 'tap operation'));
82049                         } else {
82050                             selection
82051                                 .append('kbd')
82052                                 .attr('class', 'shortcut')
82053                                 .text(function (d) { return d.shortcut; });
82054                         }
82055
82056                         if (i < nodes.length - 1) {
82057                             selection
82058                                 .append('span')
82059                                 .text(d.separator || '\u00a0' + _t('shortcuts.or') + '\u00a0');
82060                         } else if (i === nodes.length - 1 && d.suffix) {
82061                             selection
82062                                 .append('span')
82063                                 .text(d.suffix);
82064                         }
82065                     });
82066
82067
82068                 shortcutKeys
82069                     .filter(function(d) { return d.gesture; })
82070                     .each(function () {
82071                         var selection = select(this);
82072
82073                         selection
82074                             .append('span')
82075                             .text('+');
82076
82077                         selection
82078                             .append('span')
82079                             .attr('class', 'gesture')
82080                             .text(function (d) { return _t(d.gesture); });
82081                     });
82082
82083
82084                 shortcutRows
82085                     .append('td')
82086                     .attr('class', 'shortcut-desc')
82087                     .text(function (d) { return d.text ? _t(d.text) : '\u00a0'; });
82088
82089
82090                 shortcuts = shortcuts
82091                     .merge(shortcutsEnter);
82092
82093                 // Update
82094                 wrapper.selectAll('.shortcut-tab')
82095                     .style('display', function (d, i) {
82096                         return i === _activeTab ? 'flex' : 'none';
82097                     });
82098             }
82099
82100
82101             return function(selection, show) {
82102                 _selection = selection;
82103                 if (show) {
82104                     _modalSelection = uiModal(selection);
82105                     _modalSelection.call(shortcutsModal);
82106                 }
82107             };
82108         }
82109
82110         var pair_1 = pair;
82111
82112
82113         function search(input, dims) {
82114           if (!dims) { dims = 'NSEW'; }
82115           if (typeof input !== 'string') { return null; }
82116
82117           input = input.toUpperCase();
82118           var regex = /^[\s\,]*([NSEW])?\s*([\-|\—|\―]?[0-9.]+)[°º˚]?\s*(?:([0-9.]+)['’′‘]\s*)?(?:([0-9.]+)(?:''|"|”|″)\s*)?([NSEW])?/;
82119
82120           var m = input.match(regex);
82121           if (!m) { return null; }  // no match
82122
82123           var matched = m[0];
82124
82125           // extract dimension.. m[1] = leading, m[5] = trailing
82126           var dim;
82127           if (m[1] && m[5]) {                 // if matched both..
82128             dim = m[1];                       // keep leading
82129             matched = matched.slice(0, -1);   // remove trailing dimension from match
82130           } else {
82131             dim = m[1] || m[5];
82132           }
82133
82134           // if unrecognized dimension
82135           if (dim && dims.indexOf(dim) === -1) { return null; }
82136
82137           // extract DMS
82138           var deg = m[2] ? parseFloat(m[2]) : 0;
82139           var min = m[3] ? parseFloat(m[3]) / 60 : 0;
82140           var sec = m[4] ? parseFloat(m[4]) / 3600 : 0;
82141           var sign = (deg < 0) ? -1 : 1;
82142           if (dim === 'S' || dim === 'W') { sign *= -1; }
82143
82144           return {
82145             val: (Math.abs(deg) + min + sec) * sign,
82146             dim: dim,
82147             matched: matched,
82148             remain: input.slice(matched.length)
82149           };
82150         }
82151
82152
82153         function pair(input, dims) {
82154           input = input.trim();
82155           var one = search(input, dims);
82156           if (!one) { return null; }
82157
82158           input = one.remain.trim();
82159           var two = search(input, dims);
82160           if (!two || two.remain) { return null; }
82161
82162           if (one.dim) {
82163             return swapdim(one.val, two.val, one.dim);
82164           } else {
82165             return [one.val, two.val];
82166           }
82167         }
82168
82169
82170         function swapdim(a, b, dim) {
82171           if (dim === 'N' || dim === 'S') { return [a, b]; }
82172           if (dim === 'W' || dim === 'E') { return [b, a]; }
82173         }
82174
82175         function uiFeatureList(context) {
82176             var _geocodeResults;
82177
82178
82179             function featureList(selection) {
82180                 var header = selection
82181                     .append('div')
82182                     .attr('class', 'header fillL cf');
82183
82184                 header
82185                     .append('h3')
82186                     .text(_t('inspector.feature_list'));
82187
82188                 var searchWrap = selection
82189                     .append('div')
82190                     .attr('class', 'search-header');
82191
82192                 var search = searchWrap
82193                     .append('input')
82194                     .attr('placeholder', _t('inspector.search'))
82195                     .attr('type', 'search')
82196                     .call(utilNoAuto)
82197                     .on('keypress', keypress)
82198                     .on('keydown', keydown)
82199                     .on('input', inputevent);
82200
82201                 searchWrap
82202                     .call(svgIcon('#iD-icon-search', 'pre-text'));
82203
82204                 var listWrap = selection
82205                     .append('div')
82206                     .attr('class', 'inspector-body');
82207
82208                 var list = listWrap
82209                     .append('div')
82210                     .attr('class', 'feature-list cf');
82211
82212                 context
82213                     .on('exit.feature-list', clearSearch);
82214                 context.map()
82215                     .on('drawn.feature-list', mapDrawn);
82216
82217                 context.keybinding()
82218                     .on(uiCmd('⌘F'), focusSearch);
82219
82220
82221                 function focusSearch() {
82222                     var mode = context.mode() && context.mode().id;
82223                     if (mode !== 'browse') { return; }
82224
82225                     event.preventDefault();
82226                     search.node().focus();
82227                 }
82228
82229
82230                 function keydown() {
82231                     if (event.keyCode === 27) {  // escape
82232                         search.node().blur();
82233                     }
82234                 }
82235
82236
82237                 function keypress() {
82238                     var q = search.property('value'),
82239                         items = list.selectAll('.feature-list-item');
82240                     if (event.keyCode === 13 && q.length && items.size()) {  // return
82241                         click(items.datum());
82242                     }
82243                 }
82244
82245
82246                 function inputevent() {
82247                     _geocodeResults = undefined;
82248                     drawList();
82249                 }
82250
82251
82252                 function clearSearch() {
82253                     search.property('value', '');
82254                     drawList();
82255                 }
82256
82257
82258                 function mapDrawn(e) {
82259                     if (e.full) {
82260                         drawList();
82261                     }
82262                 }
82263
82264
82265                 function features() {
82266                     var result = [];
82267                     var graph = context.graph();
82268                     var visibleCenter = context.map().extent().center();
82269                     var q = search.property('value').toLowerCase();
82270
82271                     if (!q) { return result; }
82272
82273                     var idMatch = q.match(/(?:^|\W)(node|way|relation|[nwr])\W?0*([1-9]\d*)(?:\W|$)/i);
82274
82275                     if (idMatch) {
82276                         var elemType = idMatch[1].charAt(0);
82277                         var elemId = idMatch[2];
82278                         result.push({
82279                             id: elemType + elemId,
82280                             geometry: elemType === 'n' ? 'point' : elemType === 'w' ? 'line' : 'relation',
82281                             type: elemType === 'n' ? _t('inspector.node') : elemType === 'w' ? _t('inspector.way') : _t('inspector.relation'),
82282                             name: elemId
82283                         });
82284                     }
82285
82286                     var locationMatch = pair_1(q.toUpperCase()) || q.match(/^(-?\d+\.?\d*)\s+(-?\d+\.?\d*)$/);
82287
82288                     if (locationMatch) {
82289                         var loc = [parseFloat(locationMatch[0]), parseFloat(locationMatch[1])];
82290                         result.push({
82291                             id: -1,
82292                             geometry: 'point',
82293                             type: _t('inspector.location'),
82294                             name: dmsCoordinatePair([loc[1], loc[0]]),
82295                             location: loc
82296                         });
82297                     }
82298
82299                     var allEntities = graph.entities;
82300                     var localResults = [];
82301                     for (var id in allEntities) {
82302                         var entity = allEntities[id];
82303                         if (!entity) { continue; }
82304
82305                         var name = utilDisplayName(entity) || '';
82306                         if (name.toLowerCase().indexOf(q) < 0) { continue; }
82307
82308                         var matched = _mainPresetIndex.match(entity, graph);
82309                         var type = (matched && matched.name()) || utilDisplayType(entity.id);
82310                         var extent = entity.extent(graph);
82311                         var distance = extent ? geoSphericalDistance(visibleCenter, extent.center()) : 0;
82312
82313                         localResults.push({
82314                             id: entity.id,
82315                             entity: entity,
82316                             geometry: entity.geometry(graph),
82317                             type: type,
82318                             name: name,
82319                             distance: distance
82320                         });
82321
82322                         if (localResults.length > 100) { break; }
82323                     }
82324                     localResults = localResults.sort(function byDistance(a, b) {
82325                         return a.distance - b.distance;
82326                     });
82327                     result = result.concat(localResults);
82328
82329                     (_geocodeResults || []).forEach(function(d) {
82330                         if (d.osm_type && d.osm_id) {    // some results may be missing these - #1890
82331
82332                             // Make a temporary osmEntity so we can preset match
82333                             // and better localize the search result - #4725
82334                             var id = osmEntity.id.fromOSM(d.osm_type, d.osm_id);
82335                             var tags = {};
82336                             tags[d.class] = d.type;
82337
82338                             var attrs = { id: id, type: d.osm_type, tags: tags };
82339                             if (d.osm_type === 'way') {   // for ways, add some fake closed nodes
82340                                 attrs.nodes = ['a','a'];  // so that geometry area is possible
82341                             }
82342
82343                             var tempEntity = osmEntity(attrs);
82344                             var tempGraph = coreGraph([tempEntity]);
82345                             var matched = _mainPresetIndex.match(tempEntity, tempGraph);
82346                             var type = (matched && matched.name()) || utilDisplayType(id);
82347
82348                             result.push({
82349                                 id: tempEntity.id,
82350                                 geometry: tempEntity.geometry(tempGraph),
82351                                 type: type,
82352                                 name: d.display_name,
82353                                 extent: new geoExtent(
82354                                     [parseFloat(d.boundingbox[3]), parseFloat(d.boundingbox[0])],
82355                                     [parseFloat(d.boundingbox[2]), parseFloat(d.boundingbox[1])])
82356                             });
82357                         }
82358                     });
82359
82360                     if (q.match(/^[0-9]+$/)) {
82361                         // if query is just a number, possibly an OSM ID without a prefix
82362                         result.push({
82363                             id: 'n' + q,
82364                             geometry: 'point',
82365                             type: _t('inspector.node'),
82366                             name: q
82367                         });
82368                         result.push({
82369                             id: 'w' + q,
82370                             geometry: 'line',
82371                             type: _t('inspector.way'),
82372                             name: q
82373                         });
82374                         result.push({
82375                             id: 'r' + q,
82376                             geometry: 'relation',
82377                             type: _t('inspector.relation'),
82378                             name: q
82379                         });
82380                     }
82381
82382                     return result;
82383                 }
82384
82385
82386                 function drawList() {
82387                     var value = search.property('value');
82388                     var results = features();
82389
82390                     list.classed('filtered', value.length);
82391
82392                     var resultsIndicator = list.selectAll('.no-results-item')
82393                         .data([0])
82394                         .enter()
82395                         .append('button')
82396                         .property('disabled', true)
82397                         .attr('class', 'no-results-item')
82398                         .call(svgIcon('#iD-icon-alert', 'pre-text'));
82399
82400                     resultsIndicator.append('span')
82401                         .attr('class', 'entity-name');
82402
82403                     list.selectAll('.no-results-item .entity-name')
82404                         .text(_t('geocoder.no_results_worldwide'));
82405
82406                     if (services.geocoder) {
82407                       list.selectAll('.geocode-item')
82408                           .data([0])
82409                           .enter()
82410                           .append('button')
82411                           .attr('class', 'geocode-item')
82412                           .on('click', geocoderSearch)
82413                           .append('div')
82414                           .attr('class', 'label')
82415                           .append('span')
82416                           .attr('class', 'entity-name')
82417                           .text(_t('geocoder.search'));
82418                     }
82419
82420                     list.selectAll('.no-results-item')
82421                         .style('display', (value.length && !results.length) ? 'block' : 'none');
82422
82423                     list.selectAll('.geocode-item')
82424                         .style('display', (value && _geocodeResults === undefined) ? 'block' : 'none');
82425
82426                     list.selectAll('.feature-list-item')
82427                         .data([-1])
82428                         .remove();
82429
82430                     var items = list.selectAll('.feature-list-item')
82431                         .data(results, function(d) { return d.id; });
82432
82433                     var enter = items.enter()
82434                         .insert('button', '.geocode-item')
82435                         .attr('class', 'feature-list-item')
82436                         .on('mouseover', mouseover)
82437                         .on('mouseout', mouseout)
82438                         .on('click', click);
82439
82440                     var label = enter
82441                         .append('div')
82442                         .attr('class', 'label');
82443
82444                     label
82445                         .each(function(d) {
82446                             select(this)
82447                                 .call(svgIcon('#iD-icon-' + d.geometry, 'pre-text'));
82448                         });
82449
82450                     label
82451                         .append('span')
82452                         .attr('class', 'entity-type')
82453                         .text(function(d) { return d.type; });
82454
82455                     label
82456                         .append('span')
82457                         .attr('class', 'entity-name')
82458                         .text(function(d) { return d.name; });
82459
82460                     enter
82461                         .style('opacity', 0)
82462                         .transition()
82463                         .style('opacity', 1);
82464
82465                     items.order();
82466
82467                     items.exit()
82468                         .remove();
82469                 }
82470
82471
82472                 function mouseover(d) {
82473                     if (d.id === -1) { return; }
82474
82475                     utilHighlightEntities([d.id], true, context);
82476                 }
82477
82478
82479                 function mouseout(d) {
82480                     if (d.id === -1) { return; }
82481
82482                     utilHighlightEntities([d.id], false, context);
82483                 }
82484
82485
82486                 function click(d) {
82487                     event.preventDefault();
82488
82489                     if (d.location) {
82490                         context.map().centerZoomEase([d.location[1], d.location[0]], 19);
82491
82492                     } else if (d.entity) {
82493                         utilHighlightEntities([d.id], false, context);
82494
82495                         context.enter(modeSelect(context, [d.entity.id]));
82496                         context.map().zoomToEase(d.entity);
82497
82498                     } else {
82499                         // download, zoom to, and select the entity with the given ID
82500                         context.zoomToEntity(d.id);
82501                     }
82502                 }
82503
82504
82505                 function geocoderSearch() {
82506                     services.geocoder.search(search.property('value'), function (err, resp) {
82507                         _geocodeResults = resp || [];
82508                         drawList();
82509                     });
82510                 }
82511             }
82512
82513
82514             return featureList;
82515         }
82516
82517         function uiSectionEntityIssues(context) {
82518
82519             var _entityIDs = [];
82520             var _issues = [];
82521             var _activeIssueID;
82522
82523             var section = uiSection('entity-issues', context)
82524                 .shouldDisplay(function() {
82525                     return _issues.length > 0;
82526                 })
82527                 .title(function() {
82528                     return _t('issues.list_title', { count: _issues.length });
82529                 })
82530                 .disclosureContent(renderDisclosureContent);
82531
82532             context.validator()
82533                 .on('validated.entity_issues', function() {
82534                     // Refresh on validated events
82535                     reloadIssues();
82536                     section.reRender();
82537                 })
82538                 .on('focusedIssue.entity_issues', function(issue) {
82539                      makeActiveIssue(issue.id);
82540                 });
82541
82542             function reloadIssues() {
82543                 _issues = context.validator().getSharedEntityIssues(_entityIDs, { includeDisabledRules: true });
82544             }
82545
82546             function makeActiveIssue(issueID) {
82547                 _activeIssueID = issueID;
82548                 section.selection().selectAll('.issue-container')
82549                     .classed('active', function(d) { return d.id === _activeIssueID; });
82550             }
82551
82552             function renderDisclosureContent(selection) {
82553
82554                 selection.classed('grouped-items-area', true);
82555
82556                 _activeIssueID = _issues.length > 0 ? _issues[0].id : null;
82557
82558                 var containers = selection.selectAll('.issue-container')
82559                     .data(_issues, function(d) { return d.id; });
82560
82561                 // Exit
82562                 containers.exit()
82563                     .remove();
82564
82565                 // Enter
82566                 var containersEnter = containers.enter()
82567                     .append('div')
82568                     .attr('class', 'issue-container');
82569
82570
82571                 var itemsEnter = containersEnter
82572                     .append('div')
82573                     .attr('class', function(d) { return 'issue severity-' + d.severity; })
82574                     .on('mouseover.highlight', function(d) {
82575                         // don't hover-highlight the selected entity
82576                         var ids = d.entityIds
82577                             .filter(function(e) { return _entityIDs.indexOf(e) === -1; });
82578
82579                         utilHighlightEntities(ids, true, context);
82580                     })
82581                     .on('mouseout.highlight', function(d) {
82582                         var ids = d.entityIds
82583                             .filter(function(e) { return _entityIDs.indexOf(e) === -1; });
82584
82585                         utilHighlightEntities(ids, false, context);
82586                     });
82587
82588                 var labelsEnter = itemsEnter
82589                     .append('div')
82590                     .attr('class', 'issue-label')
82591                     .on('click', function(d) {
82592
82593                         makeActiveIssue(d.id); // expand only the clicked item
82594
82595                         var extent = d.extent(context.graph());
82596                         if (extent) {
82597                             var setZoom = Math.max(context.map().zoom(), 19);
82598                             context.map().unobscuredCenterZoomEase(extent.center(), setZoom);
82599                         }
82600                     });
82601
82602                 var textEnter = labelsEnter
82603                     .append('span')
82604                     .attr('class', 'issue-text');
82605
82606                 textEnter
82607                     .append('span')
82608                     .attr('class', 'issue-icon')
82609                     .each(function(d) {
82610                         var iconName = '#iD-icon-' + (d.severity === 'warning' ? 'alert' : 'error');
82611                         select(this)
82612                             .call(svgIcon(iconName));
82613                     });
82614
82615                 textEnter
82616                     .append('span')
82617                     .attr('class', 'issue-message');
82618
82619
82620                 var infoButton = labelsEnter
82621                     .append('button')
82622                     .attr('class', 'issue-info-button')
82623                     .attr('title', _t('icons.information'))
82624                     .attr('tabindex', -1)
82625                     .call(svgIcon('#iD-icon-inspect'));
82626
82627                 infoButton
82628                     .on('click', function () {
82629                         event.stopPropagation();
82630                         event.preventDefault();
82631                         this.blur();    // avoid keeping focus on the button - #4641
82632
82633                         var container = select(this.parentNode.parentNode.parentNode);
82634                         var info = container.selectAll('.issue-info');
82635                         var isExpanded = info.classed('expanded');
82636
82637                         if (isExpanded) {
82638                             info
82639                                 .transition()
82640                                 .duration(200)
82641                                 .style('max-height', '0px')
82642                                 .style('opacity', '0')
82643                                 .on('end', function () {
82644                                     info.classed('expanded', false);
82645                                 });
82646                         } else {
82647                             info
82648                                 .classed('expanded', true)
82649                                 .transition()
82650                                 .duration(200)
82651                                 .style('max-height', '200px')
82652                                 .style('opacity', '1')
82653                                 .on('end', function () {
82654                                     info.style('max-height', null);
82655                                 });
82656                         }
82657                     });
82658
82659                 itemsEnter
82660                     .append('ul')
82661                     .attr('class', 'issue-fix-list');
82662
82663                 containersEnter
82664                     .append('div')
82665                     .attr('class', 'issue-info')
82666                     .style('max-height', '0')
82667                     .style('opacity', '0')
82668                     .each(function(d) {
82669                         if (typeof d.reference === 'function') {
82670                             select(this)
82671                                 .call(d.reference);
82672                         } else {
82673                             select(this)
82674                                 .text(_t('inspector.no_documentation_key'));
82675                         }
82676                     });
82677
82678
82679                 // Update
82680                 containers = containers
82681                     .merge(containersEnter)
82682                     .classed('active', function(d) { return d.id === _activeIssueID; });
82683
82684                 containers.selectAll('.issue-message')
82685                     .text(function(d) {
82686                         return d.message(context);
82687                     });
82688
82689                 // fixes
82690                 var fixLists = containers.selectAll('.issue-fix-list');
82691
82692                 var fixes = fixLists.selectAll('.issue-fix-item')
82693                     .data(function(d) { return d.fixes ? d.fixes(context) : []; }, function(fix) { return fix.id; });
82694
82695                 fixes.exit()
82696                     .remove();
82697
82698                 var fixesEnter = fixes.enter()
82699                     .append('li')
82700                     .attr('class', 'issue-fix-item')
82701                     .on('click', function(d) {
82702                         // not all fixes are actionable
82703                         if (!select(this).classed('actionable') || !d.onClick) { return; }
82704
82705                         // Don't run another fix for this issue within a second of running one
82706                         // (Necessary for "Select a feature type" fix. Most fixes should only ever run once)
82707                         if (d.issue.dateLastRanFix && new Date() - d.issue.dateLastRanFix < 1000) { return; }
82708                         d.issue.dateLastRanFix = new Date();
82709
82710                         // remove hover-highlighting
82711                         utilHighlightEntities(d.issue.entityIds.concat(d.entityIds), false, context);
82712
82713                         new Promise(function(resolve, reject) {
82714                             d.onClick(context, resolve, reject);
82715                             if (d.onClick.length <= 1) {
82716                                 // if the fix doesn't take any completion parameters then consider it resolved
82717                                 resolve();
82718                             }
82719                         })
82720                         .then(function() {
82721                             // revalidate whenever the fix has finished running successfully
82722                             context.validator().validate();
82723                         });
82724                     })
82725                     .on('mouseover.highlight', function(d) {
82726                         utilHighlightEntities(d.entityIds, true, context);
82727                     })
82728                     .on('mouseout.highlight', function(d) {
82729                         utilHighlightEntities(d.entityIds, false, context);
82730                     });
82731
82732                 fixesEnter
82733                     .append('span')
82734                     .attr('class', 'fix-icon')
82735                     .each(function(d) {
82736                         var iconName = d.icon || 'iD-icon-wrench';
82737                         if (iconName.startsWith('maki')) {
82738                             iconName += '-15';
82739                         }
82740                         select(this).call(svgIcon('#' + iconName));
82741                     });
82742
82743                 fixesEnter
82744                     .append('span')
82745                     .attr('class', 'fix-message')
82746                     .text(function(d) { return d.title; });
82747
82748                 fixesEnter.merge(fixes)
82749                     .classed('actionable', function(d) {
82750                         return d.onClick;
82751                     })
82752                     .attr('title', function(d) {
82753                         if (d.disabledReason) {
82754                             return d.disabledReason;
82755                         }
82756                         return null;
82757                     });
82758             }
82759
82760             section.entityIDs = function(val) {
82761                 if (!arguments.length) { return _entityIDs; }
82762                 if (!_entityIDs || !val || !utilArrayIdentical(_entityIDs, val)) {
82763                     _entityIDs = val;
82764                     _activeIssueID = null;
82765                     reloadIssues();
82766                 }
82767                 return section;
82768             };
82769
82770             return section;
82771         }
82772
82773         function uiPresetIcon() {
82774           var _preset;
82775           var _geometry;
82776           var _sizeClass = 'medium';
82777
82778
82779           function isSmall() {
82780             return _sizeClass === 'small';
82781           }
82782
82783
82784           function presetIcon(selection) {
82785             selection.each(render);
82786           }
82787
82788
82789           function getIcon(p, geom) {
82790             if (isSmall() && p.isFallback && p.isFallback())
82791               { return 'iD-icon-' + p.id; }
82792             else if (p.icon)
82793               { return p.icon; }
82794             else if (geom === 'line')
82795               { return 'iD-other-line'; }
82796             else if (geom === 'vertex')
82797               { return p.isFallback() ? '' : 'temaki-vertex'; }
82798             else if (isSmall() && geom === 'point')
82799               { return ''; }
82800             else
82801               { return 'maki-marker-stroked'; }
82802           }
82803
82804
82805           function renderPointBorder(enter) {
82806             var w = 40;
82807             var h = 40;
82808
82809             enter
82810               .append('svg')
82811               .attr('class', 'preset-icon-fill preset-icon-point-border')
82812               .attr('width', w)
82813               .attr('height', h)
82814               .attr('viewBox', ("0 0 " + w + " " + h))
82815               .append('path')
82816               .attr('transform', 'translate(11.5, 8)')
82817               .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');
82818           }
82819
82820
82821           function renderCircleFill(fillEnter) {
82822             var w = 60;
82823             var h = 60;
82824             var d = 40;
82825
82826             fillEnter
82827               .append('svg')
82828               .attr('class', 'preset-icon-fill preset-icon-fill-vertex')
82829               .attr('width', w)
82830               .attr('height', h)
82831               .attr('viewBox', ("0 0 " + w + " " + h))
82832               .append('circle')
82833               .attr('cx', w / 2)
82834               .attr('cy', h / 2)
82835               .attr('r', d / 2);
82836           }
82837
82838
82839           function renderSquareFill(fillEnter) {
82840             var d = isSmall() ? 40 : 60;
82841             var w = d;
82842             var h = d;
82843             var l = d * 2/3;
82844             var c1 = (w-l) / 2;
82845             var c2 = c1 + l;
82846
82847             fillEnter = fillEnter
82848               .append('svg')
82849               .attr('class', 'preset-icon-fill preset-icon-fill-area')
82850               .attr('width', w)
82851               .attr('height', h)
82852               .attr('viewBox', ("0 0 " + w + " " + h));
82853
82854             ['fill', 'stroke'].forEach(function (klass) {
82855               fillEnter
82856                 .append('path')
82857                 .attr('d', ("M" + c1 + " " + c1 + " L" + c1 + " " + c2 + " L" + c2 + " " + c2 + " L" + c2 + " " + c1 + " Z"))
82858                 .attr('class', ("line area " + klass));
82859             });
82860
82861             var rVertex = 2.5;
82862             [[c1, c1], [c1, c2], [c2, c2], [c2, c1]].forEach(function (point) {
82863               fillEnter
82864                 .append('circle')
82865                 .attr('class', 'vertex')
82866                 .attr('cx', point[0])
82867                 .attr('cy', point[1])
82868                 .attr('r', rVertex);
82869             });
82870
82871             if (!isSmall()) {
82872               var rMidpoint = 1.25;
82873               [[c1, w/2], [c2, w/2], [h/2, c1], [h/2, c2]].forEach(function (point) {
82874                 fillEnter
82875                   .append('circle')
82876                   .attr('class', 'midpoint')
82877                   .attr('cx', point[0])
82878                   .attr('cy', point[1])
82879                   .attr('r', rMidpoint);
82880               });
82881             }
82882           }
82883
82884
82885           function renderLine(lineEnter) {
82886             var d = isSmall() ? 40 : 60;
82887             // draw the line parametrically
82888             var w = d;
82889             var h = d;
82890             var y = Math.round(d * 0.72);
82891             var l = Math.round(d * 0.6);
82892             var r = 2.5;
82893             var x1 = (w - l) / 2;
82894             var x2 = x1 + l;
82895
82896             lineEnter = lineEnter
82897               .append('svg')
82898               .attr('class', 'preset-icon-line')
82899               .attr('width', w)
82900               .attr('height', h)
82901               .attr('viewBox', ("0 0 " + w + " " + h));
82902
82903             ['casing', 'stroke'].forEach(function (klass) {
82904               lineEnter
82905                 .append('path')
82906                 .attr('d', ("M" + x1 + " " + y + " L" + x2 + " " + y))
82907                 .attr('class', ("line " + klass));
82908             });
82909
82910             [[x1-1, y], [x2+1, y]].forEach(function (point) {
82911               lineEnter
82912                 .append('circle')
82913                 .attr('class', 'vertex')
82914                 .attr('cx', point[0])
82915                 .attr('cy', point[1])
82916                 .attr('r', r);
82917             });
82918           }
82919
82920
82921           function renderRoute(routeEnter) {
82922             var d = isSmall() ? 40 : 60;
82923             // draw the route parametrically
82924             var w = d;
82925             var h = d;
82926             var y1 = Math.round(d * 0.80);
82927             var y2 = Math.round(d * 0.68);
82928             var l = Math.round(d * 0.6);
82929             var r = 2;
82930             var x1 = (w - l) / 2;
82931             var x2 = x1 + l / 3;
82932             var x3 = x2 + l / 3;
82933             var x4 = x3 + l / 3;
82934
82935             routeEnter = routeEnter
82936               .append('svg')
82937               .attr('class', 'preset-icon-route')
82938               .attr('width', w)
82939               .attr('height', h)
82940               .attr('viewBox', ("0 0 " + w + " " + h));
82941
82942             ['casing', 'stroke'].forEach(function (klass) {
82943               routeEnter
82944                 .append('path')
82945                 .attr('d', ("M" + x1 + " " + y1 + " L" + x2 + " " + y2))
82946                 .attr('class', ("segment0 line " + klass));
82947               routeEnter
82948                 .append('path')
82949                 .attr('d', ("M" + x2 + " " + y2 + " L" + x3 + " " + y1))
82950                 .attr('class', ("segment1 line " + klass));
82951               routeEnter
82952                 .append('path')
82953                 .attr('d', ("M" + x3 + " " + y1 + " L" + x4 + " " + y2))
82954                 .attr('class', ("segment2 line " + klass));
82955             });
82956
82957             [[x1, y1], [x2, y2], [x3, y1], [x4, y2]].forEach(function (point) {
82958               routeEnter
82959                 .append('circle')
82960                 .attr('class', 'vertex')
82961                 .attr('cx', point[0])
82962                 .attr('cy', point[1])
82963                 .attr('r', r);
82964             });
82965           }
82966
82967
82968           // Route icons are drawn with a zigzag annotation underneath:
82969           //     o   o
82970           //    / \ /
82971           //   o   o
82972           // This dataset defines the styles that are used to draw the zigzag segments.
82973           var routeSegments = {
82974             bicycle: ['highway/cycleway', 'highway/cycleway', 'highway/cycleway'],
82975             bus: ['highway/unclassified', 'highway/secondary', 'highway/primary'],
82976             trolleybus: ['highway/unclassified', 'highway/secondary', 'highway/primary'],
82977             detour: ['highway/tertiary', 'highway/residential', 'highway/unclassified'],
82978             ferry: ['route/ferry', 'route/ferry', 'route/ferry'],
82979             foot: ['highway/footway', 'highway/footway', 'highway/footway'],
82980             hiking: ['highway/path', 'highway/path', 'highway/path'],
82981             horse: ['highway/bridleway', 'highway/bridleway', 'highway/bridleway'],
82982             light_rail: ['railway/light_rail', 'railway/light_rail', 'railway/light_rail'],
82983             monorail: ['railway/monorail', 'railway/monorail', 'railway/monorail'],
82984             pipeline: ['man_made/pipeline', 'man_made/pipeline', 'man_made/pipeline'],
82985             piste: ['piste/downhill', 'piste/hike', 'piste/nordic'],
82986             power: ['power/line', 'power/line', 'power/line'],
82987             road: ['highway/secondary', 'highway/primary', 'highway/trunk'],
82988             subway: ['railway/subway', 'railway/subway', 'railway/subway'],
82989             train: ['railway/rail', 'railway/rail', 'railway/rail'],
82990             tram: ['railway/tram', 'railway/tram', 'railway/tram'],
82991             waterway: ['waterway/stream', 'waterway/stream', 'waterway/stream']
82992           };
82993
82994
82995           function render() {
82996             var p = _preset.apply(this, arguments);
82997             var geom = _geometry ? _geometry.apply(this, arguments) : null;
82998             if (geom === 'relation' && p.tags && ((p.tags.type === 'route' && p.tags.route && routeSegments[p.tags.route]) || p.tags.type === 'waterway')) {
82999               geom = 'route';
83000             }
83001
83002             var showThirdPartyIcons = corePreferences('preferences.privacy.thirdpartyicons') || 'true';
83003             var isFallback = isSmall() && p.isFallback && p.isFallback();
83004             var imageURL = (showThirdPartyIcons === 'true') && p.imageURL;
83005             var picon = getIcon(p, geom);
83006             var isMaki = picon && /^maki-/.test(picon);
83007             var isTemaki = picon && /^temaki-/.test(picon);
83008             var isFa = picon && /^fa[srb]-/.test(picon);
83009             var isTnp = picon && /^tnp-/.test(picon);
83010             var isiDIcon = picon && !(isMaki || isTemaki || isFa || isTnp);
83011             var isCategory = !p.setTags;
83012             var drawPoint = picon && geom === 'point' && isSmall() && !isFallback;
83013             var drawVertex = picon !== null && geom === 'vertex' && (!isSmall() || !isFallback);
83014             var drawLine = picon && geom === 'line' && !isFallback && !isCategory;
83015             var drawArea = picon && geom === 'area' && !isFallback;
83016             var drawRoute = picon && geom === 'route';
83017             var isFramed = (drawVertex || drawArea || drawLine || drawRoute);
83018
83019             var tags = !isCategory ? p.setTags({}, geom) : {};
83020             for (var k in tags) {
83021               if (tags[k] === '*') {
83022                 tags[k] = 'yes';
83023               }
83024             }
83025
83026             var tagClasses = svgTagClasses().getClassesString(tags, '');
83027             var selection = select(this);
83028
83029             var container = selection.selectAll('.preset-icon-container')
83030               .data([0]);
83031
83032             container = container.enter()
83033               .append('div')
83034               .attr('class', ("preset-icon-container " + _sizeClass))
83035               .merge(container);
83036
83037             container
83038               .classed('showing-img', !!imageURL)
83039               .classed('fallback', isFallback);
83040
83041
83042             var pointBorder = container.selectAll('.preset-icon-point-border')
83043               .data(drawPoint ? [0] : []);
83044
83045             pointBorder.exit()
83046               .remove();
83047
83048             var pointBorderEnter = pointBorder.enter();
83049             renderPointBorder(pointBorderEnter);
83050             pointBorder = pointBorderEnter.merge(pointBorder);
83051
83052
83053             var vertexFill = container.selectAll('.preset-icon-fill-vertex')
83054               .data(drawVertex ? [0] : []);
83055
83056             vertexFill.exit()
83057               .remove();
83058
83059             var vertexFillEnter = vertexFill.enter();
83060             renderCircleFill(vertexFillEnter);
83061             vertexFill = vertexFillEnter.merge(vertexFill);
83062
83063
83064             var fill = container.selectAll('.preset-icon-fill-area')
83065               .data(drawArea ? [0] : []);
83066
83067             fill.exit()
83068               .remove();
83069
83070             var fillEnter = fill.enter();
83071             renderSquareFill(fillEnter);
83072             fill = fillEnter.merge(fill);
83073
83074             fill.selectAll('path.stroke')
83075               .attr('class', ("area stroke " + tagClasses));
83076             fill.selectAll('path.fill')
83077               .attr('class', ("area fill " + tagClasses));
83078
83079
83080             var line = container.selectAll('.preset-icon-line')
83081               .data(drawLine ? [0] : []);
83082
83083             line.exit()
83084               .remove();
83085
83086             var lineEnter = line.enter();
83087             renderLine(lineEnter);
83088             line = lineEnter.merge(line);
83089
83090             line.selectAll('path.stroke')
83091               .attr('class', ("line stroke " + tagClasses));
83092             line.selectAll('path.casing')
83093               .attr('class', ("line casing " + tagClasses));
83094
83095
83096             var route = container.selectAll('.preset-icon-route')
83097               .data(drawRoute ? [0] : []);
83098
83099             route.exit()
83100               .remove();
83101
83102             var routeEnter = route.enter();
83103             renderRoute(routeEnter);
83104             route = routeEnter.merge(route);
83105
83106             if (drawRoute) {
83107               var routeType = p.tags.type === 'waterway' ? 'waterway' : p.tags.route;
83108               var segmentPresetIDs = routeSegments[routeType];
83109               for (var i in segmentPresetIDs) {
83110                 var segmentPreset = _mainPresetIndex.item(segmentPresetIDs[i]);
83111                 var segmentTagClasses = svgTagClasses().getClassesString(segmentPreset.tags, '');
83112                 route.selectAll(("path.stroke.segment" + i))
83113                   .attr('class', ("segment" + i + " line stroke " + segmentTagClasses));
83114                 route.selectAll(("path.casing.segment" + i))
83115                   .attr('class', ("segment" + i + " line casing " + segmentTagClasses));
83116               }
83117             }
83118
83119
83120             var icon = container.selectAll('.preset-icon')
83121               .data(picon ? [0] : []);
83122
83123             icon.exit()
83124               .remove();
83125
83126             icon = icon.enter()
83127               .append('div')
83128               .attr('class', 'preset-icon')
83129               .call(svgIcon(''))
83130               .merge(icon);
83131
83132             icon
83133               .attr('class', 'preset-icon ' + (geom ? geom + '-geom' : ''))
83134               .classed('framed', isFramed)
83135               .classed('preset-icon-iD', isiDIcon);
83136
83137             icon.selectAll('svg')
83138               .attr('class', 'icon ' + picon + ' ' + (!isiDIcon && geom !== 'line'  ? '' : tagClasses));
83139
83140             icon.selectAll('use')
83141               .attr('href', '#' + picon + (isMaki ? (isSmall() && geom === 'point' ? '-11' : '-15') : ''));
83142
83143             var imageIcon = container.selectAll('img.image-icon')
83144               .data(imageURL ? [0] : []);
83145
83146             imageIcon.exit()
83147               .remove();
83148
83149             imageIcon = imageIcon.enter()
83150               .append('img')
83151               .attr('class', 'image-icon')
83152               .on('load', function () { return container.classed('showing-img', true); } )
83153               .on('error', function () { return container.classed('showing-img', false); } )
83154               .merge(imageIcon);
83155
83156             imageIcon
83157               .attr('src', imageURL);
83158           }
83159
83160
83161           presetIcon.preset = function(val) {
83162             if (!arguments.length) { return _preset; }
83163             _preset = utilFunctor(val);
83164             return presetIcon;
83165           };
83166
83167
83168           presetIcon.geometry = function(val) {
83169             if (!arguments.length) { return _geometry; }
83170             _geometry = utilFunctor(val);
83171             return presetIcon;
83172           };
83173
83174
83175           presetIcon.sizeClass = function(val) {
83176             if (!arguments.length) { return _sizeClass; }
83177             _sizeClass = val;
83178             return presetIcon;
83179           };
83180
83181           return presetIcon;
83182         }
83183
83184         function uiSectionFeatureType(context) {
83185
83186             var dispatch$1 = dispatch('choose');
83187
83188             var _entityIDs = [];
83189             var _presets = [];
83190
83191             var _tagReference;
83192
83193             var section = uiSection('feature-type', context)
83194                 .title(_t('inspector.feature_type'))
83195                 .disclosureContent(renderDisclosureContent);
83196
83197             function renderDisclosureContent(selection) {
83198
83199                 selection.classed('preset-list-item', true);
83200                 selection.classed('mixed-types', _presets.length > 1);
83201
83202                 var presetButtonWrap = selection
83203                     .selectAll('.preset-list-button-wrap')
83204                     .data([0])
83205                     .enter()
83206                     .append('div')
83207                     .attr('class', 'preset-list-button-wrap');
83208
83209                 var presetButton = presetButtonWrap
83210                     .append('button')
83211                     .attr('class', 'preset-list-button preset-reset')
83212                     .call(uiTooltip()
83213                         .title(_t('inspector.back_tooltip'))
83214                         .placement('bottom')
83215                     );
83216
83217                 presetButton.append('div')
83218                     .attr('class', 'preset-icon-container');
83219
83220                 presetButton
83221                     .append('div')
83222                     .attr('class', 'label')
83223                     .append('div')
83224                     .attr('class', 'label-inner');
83225
83226                 presetButtonWrap.append('div')
83227                     .attr('class', 'accessory-buttons');
83228
83229                 var tagReferenceBodyWrap = selection
83230                     .selectAll('.tag-reference-body-wrap')
83231                     .data([0]);
83232
83233                 tagReferenceBodyWrap = tagReferenceBodyWrap
83234                     .enter()
83235                     .append('div')
83236                     .attr('class', 'tag-reference-body-wrap')
83237                     .merge(tagReferenceBodyWrap);
83238
83239                 // update header
83240                 if (_tagReference) {
83241                     selection.selectAll('.preset-list-button-wrap .accessory-buttons')
83242                         .style('display', _presets.length === 1 ? null : 'none')
83243                         .call(_tagReference.button);
83244
83245                     tagReferenceBodyWrap
83246                         .style('display', _presets.length === 1 ? null : 'none')
83247                         .call(_tagReference.body);
83248                 }
83249
83250                 selection.selectAll('.preset-reset')
83251                     .on('click', function() {
83252                          dispatch$1.call('choose', this, _presets);
83253                     })
83254                     .on('pointerdown pointerup mousedown mouseup', function() {
83255                         event.preventDefault();
83256                         event.stopPropagation();
83257                     });
83258
83259                 var geometries = entityGeometries();
83260                 selection.select('.preset-list-item button')
83261                     .call(uiPresetIcon()
83262                         .geometry(_presets.length === 1 ? (geometries.length === 1 && geometries[0]) : null)
83263                         .preset(_presets.length === 1 ? _presets[0] : _mainPresetIndex.item('point'))
83264                     );
83265
83266                 // NOTE: split on en-dash, not a hyphen (to avoid conflict with hyphenated names)
83267                 var names = _presets.length === 1 ? _presets[0].name().split(' – ') : [_t('inspector.multiple_types')];
83268
83269                 var label = selection.select('.label-inner');
83270                 var nameparts = label.selectAll('.namepart')
83271                     .data(names, function(d) { return d; });
83272
83273                 nameparts.exit()
83274                     .remove();
83275
83276                 nameparts
83277                     .enter()
83278                     .append('div')
83279                     .attr('class', 'namepart')
83280                     .text(function(d) { return d; });
83281             }
83282
83283             section.entityIDs = function(val) {
83284                 if (!arguments.length) { return _entityIDs; }
83285                 _entityIDs = val;
83286                 return section;
83287             };
83288
83289             section.presets = function(val) {
83290                 if (!arguments.length) { return _presets; }
83291
83292                 // don't reload the same preset
83293                 if (!utilArrayIdentical(val, _presets)) {
83294                     _presets = val;
83295
83296                     if (_presets.length === 1) {
83297                         _tagReference = uiTagReference(_presets[0].reference())
83298                             .showing(false);
83299                     }
83300                 }
83301
83302                 return section;
83303             };
83304
83305             function entityGeometries() {
83306
83307                 var counts = {};
83308
83309                 for (var i in _entityIDs) {
83310                     var geometry = context.graph().geometry(_entityIDs[i]);
83311                     if (!counts[geometry]) { counts[geometry] = 0; }
83312                     counts[geometry] += 1;
83313                 }
83314
83315                 return Object.keys(counts).sort(function(geom1, geom2) {
83316                     return counts[geom2] - counts[geom1];
83317                 });
83318             }
83319
83320             return utilRebind(section, dispatch$1, 'on');
83321         }
83322
83323         // This currently only works with the 'restrictions' field
83324         // It borrows some code from uiHelp
83325
83326         function uiFieldHelp(context, fieldName) {
83327             var fieldHelp = {};
83328             var _inspector = select(null);
83329             var _wrap = select(null);
83330             var _body = select(null);
83331
83332             var fieldHelpKeys = {
83333                 restrictions: [
83334                     ['about',[
83335                         'about',
83336                         'from_via_to',
83337                         'maxdist',
83338                         'maxvia'
83339                     ]],
83340                     ['inspecting',[
83341                         'about',
83342                         'from_shadow',
83343                         'allow_shadow',
83344                         'restrict_shadow',
83345                         'only_shadow',
83346                         'restricted',
83347                         'only'
83348                     ]],
83349                     ['modifying',[
83350                         'about',
83351                         'indicators',
83352                         'allow_turn',
83353                         'restrict_turn',
83354                         'only_turn'
83355                     ]],
83356                     ['tips',[
83357                         'simple',
83358                         'simple_example',
83359                         'indirect',
83360                         'indirect_example',
83361                         'indirect_noedit'
83362                     ]]
83363                 ]
83364             };
83365
83366             var fieldHelpHeadings = {};
83367
83368             var replacements = {
83369                 distField: _t('restriction.controls.distance'),
83370                 viaField: _t('restriction.controls.via'),
83371                 fromShadow: icon('#iD-turn-shadow', 'pre-text shadow from'),
83372                 allowShadow: icon('#iD-turn-shadow', 'pre-text shadow allow'),
83373                 restrictShadow: icon('#iD-turn-shadow', 'pre-text shadow restrict'),
83374                 onlyShadow: icon('#iD-turn-shadow', 'pre-text shadow only'),
83375                 allowTurn: icon('#iD-turn-yes', 'pre-text turn'),
83376                 restrictTurn: icon('#iD-turn-no', 'pre-text turn'),
83377                 onlyTurn: icon('#iD-turn-only', 'pre-text turn')
83378             };
83379
83380
83381             // For each section, squash all the texts into a single markdown document
83382             var docs = fieldHelpKeys[fieldName].map(function(key) {
83383                 var helpkey = 'help.field.' + fieldName + '.' + key[0];
83384                 var text = key[1].reduce(function(all, part) {
83385                     var subkey = helpkey + '.' + part;
83386                     var depth = fieldHelpHeadings[subkey];                     // is this subkey a heading?
83387                     var hhh = depth ? Array(depth + 1).join('#') + ' ' : '';   // if so, prepend with some ##'s
83388                     return all + hhh + _t(subkey, replacements) + '\n\n';
83389                 }, '');
83390
83391                 return {
83392                     key: helpkey,
83393                     title: _t(helpkey + '.title'),
83394                     html: marked_1(text.trim())
83395                 };
83396             });
83397
83398
83399             function show() {
83400                 updatePosition();
83401
83402                 _body
83403                     .classed('hide', false)
83404                     .style('opacity', '0')
83405                     .transition()
83406                     .duration(200)
83407                     .style('opacity', '1');
83408             }
83409
83410
83411             function hide() {
83412                 _body
83413                     .classed('hide', true)
83414                     .transition()
83415                     .duration(200)
83416                     .style('opacity', '0')
83417                     .on('end', function () {
83418                         _body.classed('hide', true);
83419                     });
83420             }
83421
83422
83423             function clickHelp(index) {
83424                 var d = docs[index];
83425                 var tkeys = fieldHelpKeys[fieldName][index][1];
83426
83427                 _body.selectAll('.field-help-nav-item')
83428                     .classed('active', function(d, i) { return i === index; });
83429
83430                 var content = _body.selectAll('.field-help-content')
83431                     .html(d.html);
83432
83433                 // class the paragraphs so we can find and style them
83434                 content.selectAll('p')
83435                     .attr('class', function(d, i) { return tkeys[i]; });
83436
83437                 // insert special content for certain help sections
83438                 if (d.key === 'help.field.restrictions.inspecting') {
83439                     content
83440                         .insert('img', 'p.from_shadow')
83441                         .attr('class', 'field-help-image cf')
83442                         .attr('src', context.imagePath('tr_inspect.gif'));
83443
83444                 } else if (d.key === 'help.field.restrictions.modifying') {
83445                     content
83446                         .insert('img', 'p.allow_turn')
83447                         .attr('class', 'field-help-image cf')
83448                         .attr('src', context.imagePath('tr_modify.gif'));
83449                 }
83450             }
83451
83452
83453             fieldHelp.button = function(selection) {
83454                 if (_body.empty()) { return; }
83455
83456                 var button = selection.selectAll('.field-help-button')
83457                     .data([0]);
83458
83459                 // enter/update
83460                 button.enter()
83461                     .append('button')
83462                     .attr('class', 'field-help-button')
83463                     .attr('tabindex', -1)
83464                     .call(svgIcon('#iD-icon-help'))
83465                     .merge(button)
83466                     .on('click', function () {
83467                         event.stopPropagation();
83468                         event.preventDefault();
83469                         if (_body.classed('hide')) {
83470                             show();
83471                         } else {
83472                             hide();
83473                         }
83474                     });
83475             };
83476
83477
83478             function updatePosition() {
83479                 var wrap = _wrap.node();
83480                 var inspector = _inspector.node();
83481                 var wRect = wrap.getBoundingClientRect();
83482                 var iRect = inspector.getBoundingClientRect();
83483
83484                 _body
83485                     .style('top', wRect.top + inspector.scrollTop - iRect.top + 'px');
83486             }
83487
83488
83489             fieldHelp.body = function(selection) {
83490                 // This control expects the field to have a form-field-input-wrap div
83491                 _wrap = selection.selectAll('.form-field-input-wrap');
83492                 if (_wrap.empty()) { return; }
83493
83494                 // absolute position relative to the inspector, so it "floats" above the fields
83495                 _inspector = context.container().select('.sidebar .entity-editor-pane .inspector-body');
83496                 if (_inspector.empty()) { return; }
83497
83498                 _body = _inspector.selectAll('.field-help-body')
83499                     .data([0]);
83500
83501                 var enter = _body.enter()
83502                     .append('div')
83503                     .attr('class', 'field-help-body hide');   // initially hidden
83504
83505                 var titleEnter = enter
83506                     .append('div')
83507                     .attr('class', 'field-help-title cf');
83508
83509                 titleEnter
83510                     .append('h2')
83511                     .attr('class', ((_mainLocalizer.textDirection() === 'rtl') ? 'fr' : 'fl'))
83512                     .text(_t('help.field.' + fieldName + '.title'));
83513
83514                 titleEnter
83515                     .append('button')
83516                     .attr('class', 'fr close')
83517                     .on('click', function() {
83518                         event.stopPropagation();
83519                         event.preventDefault();
83520                         hide();
83521                     })
83522                     .call(svgIcon('#iD-icon-close'));
83523
83524                 var navEnter = enter
83525                     .append('div')
83526                     .attr('class', 'field-help-nav cf');
83527
83528                 var titles = docs.map(function(d) { return d.title; });
83529                 navEnter.selectAll('.field-help-nav-item')
83530                     .data(titles)
83531                     .enter()
83532                     .append('div')
83533                     .attr('class', 'field-help-nav-item')
83534                     .text(function(d) { return d; })
83535                     .on('click', function(d, i) {
83536                         event.stopPropagation();
83537                         event.preventDefault();
83538                         clickHelp(i);
83539                     });
83540
83541                 enter
83542                     .append('div')
83543                     .attr('class', 'field-help-content');
83544
83545                 _body = _body
83546                     .merge(enter);
83547
83548                 clickHelp(0);
83549             };
83550
83551
83552             return fieldHelp;
83553         }
83554
83555         function uiFieldCheck(field, context) {
83556             var dispatch$1 = dispatch('change');
83557             var options = field.strings && field.strings.options;
83558             var values = [];
83559             var texts = [];
83560
83561             var _tags;
83562
83563             var input = select(null);
83564             var text = select(null);
83565             var label = select(null);
83566             var reverser = select(null);
83567
83568             var _impliedYes;
83569             var _entityIDs = [];
83570             var _value;
83571
83572
83573             if (options) {
83574                 for (var k in options) {
83575                     values.push(k === 'undefined' ? undefined : k);
83576                     texts.push(field.t('options.' + k, { 'default': options[k] }));
83577                 }
83578             } else {
83579                 values = [undefined, 'yes'];
83580                 texts = [_t('inspector.unknown'), _t('inspector.check.yes')];
83581                 if (field.type !== 'defaultCheck') {
83582                     values.push('no');
83583                     texts.push(_t('inspector.check.no'));
83584                 }
83585             }
83586
83587
83588             // Checks tags to see whether an undefined value is "Assumed to be Yes"
83589             function checkImpliedYes() {
83590                 _impliedYes = (field.id === 'oneway_yes');
83591
83592                 // hack: pretend `oneway` field is a `oneway_yes` field
83593                 // where implied oneway tag exists (e.g. `junction=roundabout`) #2220, #1841
83594                 if (field.id === 'oneway') {
83595                     var entity = context.entity(_entityIDs[0]);
83596                     for (var key in entity.tags) {
83597                         if (key in osmOneWayTags && (entity.tags[key] in osmOneWayTags[key])) {
83598                             _impliedYes = true;
83599                             texts[0] = _t('presets.fields.oneway_yes.options.undefined');
83600                             break;
83601                         }
83602                     }
83603                 }
83604             }
83605
83606
83607             function reverserHidden() {
83608                 if (!context.container().select('div.inspector-hover').empty()) { return true; }
83609                 return !(_value === 'yes' || (_impliedYes && !_value));
83610             }
83611
83612
83613             function reverserSetText(selection) {
83614                 var entity = _entityIDs.length && context.hasEntity(_entityIDs[0]);
83615                 if (reverserHidden() || !entity) { return selection; }
83616
83617                 var first = entity.first();
83618                 var last = entity.isClosed() ? entity.nodes[entity.nodes.length - 2] : entity.last();
83619                 var pseudoDirection = first < last;
83620                 var icon = pseudoDirection ? '#iD-icon-forward' : '#iD-icon-backward';
83621
83622                 selection.selectAll('.reverser-span')
83623                     .text(_t('inspector.check.reverser'))
83624                     .call(svgIcon(icon, 'inline'));
83625
83626                 return selection;
83627             }
83628
83629
83630             var check = function(selection) {
83631                 checkImpliedYes();
83632
83633                 label = selection.selectAll('.form-field-input-wrap')
83634                     .data([0]);
83635
83636                 var enter = label.enter()
83637                     .append('label')
83638                     .attr('class', 'form-field-input-wrap form-field-input-check');
83639
83640                 enter
83641                     .append('input')
83642                     .property('indeterminate', field.type !== 'defaultCheck')
83643                     .attr('type', 'checkbox')
83644                     .attr('id', field.domId);
83645
83646                 enter
83647                     .append('span')
83648                     .text(texts[0])
83649                     .attr('class', 'value');
83650
83651                 if (field.type === 'onewayCheck') {
83652                     enter
83653                         .append('a')
83654                         .attr('class', 'reverser button' + (reverserHidden() ? ' hide' : ''))
83655                         .attr('href', '#')
83656                         .append('span')
83657                         .attr('class', 'reverser-span');
83658                 }
83659
83660                 label = label.merge(enter);
83661                 input = label.selectAll('input');
83662                 text = label.selectAll('span.value');
83663
83664                 input
83665                     .on('click', function() {
83666                         event.stopPropagation();
83667                         var t = {};
83668
83669                         if (Array.isArray(_tags[field.key])) {
83670                             if (values.indexOf('yes') !== -1) {
83671                                 t[field.key] = 'yes';
83672                             } else {
83673                                 t[field.key] = values[0];
83674                             }
83675                         } else {
83676                             t[field.key] = values[(values.indexOf(_value) + 1) % values.length];
83677                         }
83678
83679                         // Don't cycle through `alternating` or `reversible` states - #4970
83680                         // (They are supported as translated strings, but should not toggle with clicks)
83681                         if (t[field.key] === 'reversible' || t[field.key] === 'alternating') {
83682                             t[field.key] = values[0];
83683                         }
83684
83685                         dispatch$1.call('change', this, t);
83686                     });
83687
83688                 if (field.type === 'onewayCheck') {
83689                     reverser = label.selectAll('.reverser');
83690
83691                     reverser
83692                         .call(reverserSetText)
83693                         .on('click', function() {
83694                             event.preventDefault();
83695                             event.stopPropagation();
83696                             context.perform(
83697                                 function(graph) {
83698                                     for (var i in _entityIDs) {
83699                                         graph = actionReverse(_entityIDs[i])(graph);
83700                                     }
83701                                     return graph;
83702                                 },
83703                                 _t('operations.reverse.annotation')
83704                             );
83705
83706                             // must manually revalidate since no 'change' event was called
83707                             context.validator().validate();
83708
83709                             select(this)
83710                                 .call(reverserSetText);
83711                         });
83712                 }
83713             };
83714
83715
83716             check.entityIDs = function(val) {
83717                 if (!arguments.length) { return _entityIDs; }
83718                 _entityIDs = val;
83719                 return check;
83720             };
83721
83722
83723             check.tags = function(tags) {
83724
83725                 _tags = tags;
83726
83727                 function isChecked(val) {
83728                     return val !== 'no' && val !== '' && val !== undefined && val !== null;
83729                 }
83730
83731                 function textFor(val) {
83732                     if (val === '') { val = undefined; }
83733                     var index = values.indexOf(val);
83734                     return (index !== -1 ? texts[index] : ('"' + val + '"'));
83735                 }
83736
83737                 checkImpliedYes();
83738
83739                 var isMixed = Array.isArray(tags[field.key]);
83740
83741                 _value = !isMixed && tags[field.key] && tags[field.key].toLowerCase();
83742
83743                 if (field.type === 'onewayCheck' && (_value === '1' || _value === '-1')) {
83744                     _value = 'yes';
83745                 }
83746
83747                 input
83748                     .property('indeterminate', isMixed || (field.type !== 'defaultCheck' && !_value))
83749                     .property('checked', isChecked(_value));
83750
83751                 text
83752                     .text(isMixed ? _t('inspector.multiple_values') : textFor(_value))
83753                     .classed('mixed', isMixed);
83754
83755                 label
83756                     .classed('set', !!_value);
83757
83758                 if (field.type === 'onewayCheck') {
83759                     reverser
83760                         .classed('hide', reverserHidden())
83761                         .call(reverserSetText);
83762                 }
83763             };
83764
83765
83766             check.focus = function() {
83767                 input.node().focus();
83768             };
83769
83770             return utilRebind(check, dispatch$1, 'on');
83771         }
83772
83773         function uiFieldCombo(field, context) {
83774             var dispatch$1 = dispatch('change');
83775             var taginfo = services.taginfo;
83776             var isMulti = (field.type === 'multiCombo');
83777             var isNetwork = (field.type === 'networkCombo');
83778             var isSemi = (field.type === 'semiCombo');
83779             var optstrings = field.strings && field.strings.options;
83780             var optarray = field.options;
83781             var snake_case = (field.snake_case || (field.snake_case === undefined));
83782             var caseSensitive = field.caseSensitive;
83783             var combobox = uiCombobox(context, 'combo-' + field.safeid)
83784                 .caseSensitive(caseSensitive)
83785                 .minItems(isMulti || isSemi ? 1 : 2);
83786             var container = select(null);
83787             var inputWrap = select(null);
83788             var input = select(null);
83789             var _comboData = [];
83790             var _multiData = [];
83791             var _entityIDs = [];
83792             var _tags;
83793             var _countryCode;
83794             var _staticPlaceholder;
83795
83796             // initialize deprecated tags array
83797             var _dataDeprecated = [];
83798             _mainFileFetcher.get('deprecated')
83799                 .then(function(d) { _dataDeprecated = d; })
83800                 .catch(function() { /* ignore */ });
83801
83802
83803             // ensure multiCombo field.key ends with a ':'
83804             if (isMulti && /[^:]$/.test(field.key)) {
83805                 field.key += ':';
83806             }
83807
83808
83809             function snake(s) {
83810                 return s.replace(/\s+/g, '_');
83811             }
83812
83813             function unsnake(s) {
83814                 return s.replace(/_+/g, ' ');
83815             }
83816
83817             function clean(s) {
83818                 return s.split(';')
83819                     .map(function(s) { return s.trim(); })
83820                     .join(';');
83821             }
83822
83823
83824             // returns the tag value for a display value
83825             // (for multiCombo, dval should be the key suffix, not the entire key)
83826             function tagValue(dval) {
83827                 dval = clean(dval || '');
83828
83829                 if (optstrings) {
83830                     var found = _comboData.find(function(o) {
83831                         return o.key && clean(o.value) === dval;
83832                     });
83833                     if (found) {
83834                         return found.key;
83835                     }
83836                 }
83837
83838                 if (field.type === 'typeCombo' && !dval) {
83839                     return 'yes';
83840                 }
83841
83842                 return (snake_case ? snake(dval) : dval) || undefined;
83843             }
83844
83845
83846             // returns the display value for a tag value
83847             // (for multiCombo, tval should be the key suffix, not the entire key)
83848             function displayValue(tval) {
83849                 tval = tval || '';
83850
83851                 if (optstrings) {
83852                     var found = _comboData.find(function(o) {
83853                         return o.key === tval && o.value;
83854                     });
83855                     if (found) {
83856                         return found.value;
83857                     }
83858                 }
83859
83860                 if (field.type === 'typeCombo' && tval.toLowerCase() === 'yes') {
83861                     return '';
83862                 }
83863
83864                 return snake_case ? unsnake(tval) : tval;
83865             }
83866
83867
83868             // Compute the difference between arrays of objects by `value` property
83869             //
83870             // objectDifference([{value:1}, {value:2}, {value:3}], [{value:2}])
83871             // > [{value:1}, {value:3}]
83872             //
83873             function objectDifference(a, b) {
83874                 return a.filter(function(d1) {
83875                     return !b.some(function(d2) {
83876                         return !d2.isMixed && d1.value === d2.value;
83877                     });
83878                 });
83879             }
83880
83881
83882             function initCombo(selection, attachTo) {
83883                 if (optstrings) {
83884                     selection.attr('readonly', 'readonly');
83885                     selection.call(combobox, attachTo);
83886                     setStaticValues(setPlaceholder);
83887
83888                 } else if (optarray) {
83889                     selection.call(combobox, attachTo);
83890                     setStaticValues(setPlaceholder);
83891
83892                 } else if (taginfo) {
83893                     selection.call(combobox.fetcher(setTaginfoValues), attachTo);
83894                     setTaginfoValues('', setPlaceholder);
83895                 }
83896             }
83897
83898
83899             function setStaticValues(callback) {
83900                 if (!(optstrings || optarray)) { return; }
83901
83902                 if (optstrings) {
83903                     _comboData = Object.keys(optstrings).map(function(k) {
83904                         var v = field.t('options.' + k, { 'default': optstrings[k] });
83905                         return {
83906                             key: k,
83907                             value: v,
83908                             title: v
83909                         };
83910                     });
83911
83912                 } else if (optarray) {
83913                     _comboData = optarray.map(function(k) {
83914                         var v = snake_case ? unsnake(k) : k;
83915                         return {
83916                             key: k,
83917                             value: v,
83918                             title: v
83919                         };
83920                     });
83921                 }
83922
83923                 combobox.data(objectDifference(_comboData, _multiData));
83924                 if (callback) { callback(_comboData); }
83925             }
83926
83927
83928             function setTaginfoValues(q, callback) {
83929                 var fn = isMulti ? 'multikeys' : 'values';
83930                 var query = (isMulti ? field.key : '') + q;
83931                 var hasCountryPrefix = isNetwork && _countryCode && _countryCode.indexOf(q.toLowerCase()) === 0;
83932                 if (hasCountryPrefix) {
83933                     query = _countryCode + ':';
83934                 }
83935
83936                 var params = {
83937                     debounce: (q !== ''),
83938                     key: field.key,
83939                     query: query
83940                 };
83941
83942                 if (_entityIDs.length) {
83943                     params.geometry = context.graph().geometry(_entityIDs[0]);
83944                 }
83945
83946                 taginfo[fn](params, function(err, data) {
83947                     if (err) { return; }
83948
83949                     data = data.filter(function(d) {
83950
83951                         if (field.type === 'typeCombo' && d.value === 'yes') {
83952                             // don't show the fallback value
83953                             return false;
83954                         }
83955
83956                         // don't show values with very low usage
83957                         return !d.count || d.count > 10;
83958                     });
83959
83960                     var deprecatedValues = osmEntity.deprecatedTagValuesByKey(_dataDeprecated)[field.key];
83961                     if (deprecatedValues) {
83962                         // don't suggest deprecated tag values
83963                         data = data.filter(function(d) {
83964                             return deprecatedValues.indexOf(d.value) === -1;
83965                         });
83966                     }
83967
83968                     if (hasCountryPrefix) {
83969                         data = data.filter(function(d) {
83970                             return d.value.toLowerCase().indexOf(_countryCode + ':') === 0;
83971                         });
83972                     }
83973
83974                     // hide the caret if there are no suggestions
83975                     container.classed('empty-combobox', data.length === 0);
83976
83977                     _comboData = data.map(function(d) {
83978                         var k = d.value;
83979                         if (isMulti) { k = k.replace(field.key, ''); }
83980                         var v = snake_case ? unsnake(k) : k;
83981                         return {
83982                             key: k,
83983                             value: v,
83984                             title: isMulti ? v : d.title
83985                         };
83986                     });
83987
83988                     _comboData = objectDifference(_comboData, _multiData);
83989                     if (callback) { callback(_comboData); }
83990                 });
83991             }
83992
83993
83994             function setPlaceholder(values) {
83995
83996                 if (isMulti || isSemi) {
83997                     _staticPlaceholder = field.placeholder() || _t('inspector.add');
83998                 } else {
83999                     var vals = values
84000                         .map(function(d) { return d.value; })
84001                         .filter(function(s) { return s.length < 20; });
84002
84003                     var placeholders = vals.length > 1 ? vals : values.map(function(d) { return d.key; });
84004                     _staticPlaceholder = field.placeholder() || placeholders.slice(0, 3).join(', ');
84005                 }
84006
84007                 if (!/(…|\.\.\.)$/.test(_staticPlaceholder)) {
84008                     _staticPlaceholder += '…';
84009                 }
84010
84011                 var ph;
84012                 if (!isMulti && !isSemi && _tags && Array.isArray(_tags[field.key])) {
84013                     ph = _t('inspector.multiple_values');
84014                 } else {
84015                     ph =  _staticPlaceholder;
84016                 }
84017
84018                 container.selectAll('input')
84019                     .attr('placeholder', ph);
84020             }
84021
84022
84023             function change() {
84024                 var t = {};
84025                 var val;
84026
84027                 if (isMulti || isSemi) {
84028                     val = tagValue(utilGetSetValue(input).replace(/,/g, ';')) || '';
84029                     container.classed('active', false);
84030                     utilGetSetValue(input, '');
84031
84032                     var vals = val.split(';').filter(Boolean);
84033                     if (!vals.length) { return; }
84034
84035                     if (isMulti) {
84036                         utilArrayUniq(vals).forEach(function(v) {
84037                             var key = field.key + v;
84038                             if (_tags) {
84039                                 // don't set a multicombo value to 'yes' if it already has a non-'no' value
84040                                 // e.g. `language:de=main`
84041                                 var old = _tags[key];
84042                                 if (typeof old === 'string' && old.toLowerCase() !== 'no') { return; }
84043                             }
84044                             key = context.cleanTagKey(key);
84045                             field.keys.push(key);
84046                             t[key] = 'yes';
84047                         });
84048
84049                     } else if (isSemi) {
84050                         var arr = _multiData.map(function(d) { return d.key; });
84051                         arr = arr.concat(vals);
84052                         t[field.key] = context.cleanTagValue(utilArrayUniq(arr).filter(Boolean).join(';'));
84053                     }
84054
84055                     window.setTimeout(function() { input.node().focus(); }, 10);
84056
84057                 } else {
84058                     var rawValue = utilGetSetValue(input);
84059
84060                     // don't override multiple values with blank string
84061                     if (!rawValue && Array.isArray(_tags[field.key])) { return; }
84062
84063                     val = context.cleanTagValue(tagValue(rawValue));
84064                     t[field.key] = val || undefined;
84065                 }
84066
84067                 dispatch$1.call('change', this, t);
84068             }
84069
84070
84071             function removeMultikey(d) {
84072                 event.stopPropagation();
84073                 var t = {};
84074                 if (isMulti) {
84075                     t[d.key] = undefined;
84076                 } else if (isSemi) {
84077                     var arr = _multiData.map(function(md) {
84078                         return md.key === d.key ? null : md.key;
84079                     }).filter(Boolean);
84080
84081                     arr = utilArrayUniq(arr);
84082                     t[field.key] = arr.length ? arr.join(';') : undefined;
84083                 }
84084                 dispatch$1.call('change', this, t);
84085             }
84086
84087
84088             function combo(selection) {
84089                 container = selection.selectAll('.form-field-input-wrap')
84090                     .data([0]);
84091
84092                 var type = (isMulti || isSemi) ? 'multicombo': 'combo';
84093                 container = container.enter()
84094                     .append('div')
84095                     .attr('class', 'form-field-input-wrap form-field-input-' + type)
84096                     .merge(container);
84097
84098                 if (isMulti || isSemi) {
84099                     container = container.selectAll('.chiplist')
84100                         .data([0]);
84101
84102                     var listClass = 'chiplist';
84103
84104                     // Use a separate line for each value in the Destinations field
84105                     // to mimic highway exit signs
84106                     if (field.key === 'destination') {
84107                         listClass += ' full-line-chips';
84108                     }
84109
84110                     container = container.enter()
84111                         .append('ul')
84112                         .attr('class', listClass)
84113                         .on('click', function() {
84114                             window.setTimeout(function() { input.node().focus(); }, 10);
84115                         })
84116                         .merge(container);
84117
84118
84119                     inputWrap = container.selectAll('.input-wrap')
84120                         .data([0]);
84121
84122                     inputWrap = inputWrap.enter()
84123                         .append('li')
84124                         .attr('class', 'input-wrap')
84125                         .merge(inputWrap);
84126
84127                     input = inputWrap.selectAll('input')
84128                         .data([0]);
84129                 } else {
84130                     input = container.selectAll('input')
84131                         .data([0]);
84132                 }
84133
84134                 input = input.enter()
84135                     .append('input')
84136                     .attr('type', 'text')
84137                     .attr('id', field.domId)
84138                     .call(utilNoAuto)
84139                     .call(initCombo, selection)
84140                     .merge(input);
84141
84142                 if (isNetwork) {
84143                     var extent = combinedEntityExtent();
84144                     var countryCode = extent && iso1A2Code(extent.center());
84145                     _countryCode = countryCode && countryCode.toLowerCase();
84146                 }
84147
84148                 input
84149                     .on('change', change)
84150                     .on('blur', change);
84151
84152                 input
84153                     .on('keydown.field', function() {
84154                         switch (event.keyCode) {
84155                             case 13: // ↩ Return
84156                                 input.node().blur(); // blurring also enters the value
84157                                 event.stopPropagation();
84158                                 break;
84159                         }
84160                     });
84161
84162                 if (isMulti || isSemi) {
84163                     combobox
84164                         .on('accept', function() {
84165                             input.node().blur();
84166                             input.node().focus();
84167                         });
84168
84169                     input
84170                         .on('focus', function() { container.classed('active', true); });
84171                 }
84172             }
84173
84174
84175             combo.tags = function(tags) {
84176                 _tags = tags;
84177
84178                 if (isMulti || isSemi) {
84179                     _multiData = [];
84180
84181                     var maxLength;
84182
84183                     if (isMulti) {
84184                         // Build _multiData array containing keys already set..
84185                         for (var k in tags) {
84186                             if (k.indexOf(field.key) !== 0) { continue; }
84187                             var v = tags[k];
84188                             if (!v || (typeof v === 'string' && v.toLowerCase() === 'no')) { continue; }
84189
84190                             var suffix = k.substring(field.key.length);
84191                             _multiData.push({
84192                                 key: k,
84193                                 value: displayValue(suffix),
84194                                 isMixed: Array.isArray(v)
84195                             });
84196                         }
84197
84198                         // Set keys for form-field modified (needed for undo and reset buttons)..
84199                         field.keys = _multiData.map(function(d) { return d.key; });
84200
84201                         // limit the input length so it fits after prepending the key prefix
84202                         maxLength = context.maxCharsForTagKey() - utilUnicodeCharsCount(field.key);
84203
84204                     } else if (isSemi) {
84205
84206                         var allValues = [];
84207                         var commonValues;
84208                         if (Array.isArray(tags[field.key])) {
84209
84210                             tags[field.key].forEach(function(tagVal) {
84211                                 var thisVals = utilArrayUniq((tagVal || '').split(';')).filter(Boolean);
84212                                 allValues = allValues.concat(thisVals);
84213                                 if (!commonValues) {
84214                                     commonValues = thisVals;
84215                                 } else {
84216                                     commonValues = commonValues.filter(function (value) { return thisVals.includes(value); });
84217                                 }
84218                             });
84219                             allValues = utilArrayUniq(allValues).filter(Boolean);
84220
84221                         } else {
84222                             allValues =  utilArrayUniq((tags[field.key] || '').split(';')).filter(Boolean);
84223                             commonValues = allValues;
84224                         }
84225
84226                         _multiData = allValues.map(function(v) {
84227                             return {
84228                                 key: v,
84229                                 value: displayValue(v),
84230                                 isMixed: !commonValues.includes(v)
84231                             };
84232                         });
84233
84234                         var currLength = utilUnicodeCharsCount(commonValues.join(';'));
84235
84236                         // limit the input length to the remaining available characters
84237                         maxLength = context.maxCharsForTagValue() - currLength;
84238
84239                         if (currLength > 0) {
84240                             // account for the separator if a new value will be appended to existing
84241                             maxLength -= 1;
84242                         }
84243                     }
84244                     // a negative maxlength doesn't make sense
84245                     maxLength = Math.max(0, maxLength);
84246
84247                     var allowDragAndDrop = isSemi // only semiCombo values are ordered
84248                         && !Array.isArray(tags[field.key]);
84249
84250                     // Exclude existing multikeys from combo options..
84251                     var available = objectDifference(_comboData, _multiData);
84252                     combobox.data(available);
84253
84254                     // Hide 'Add' button if this field uses fixed set of
84255                     // translateable optstrings and they're all currently used,
84256                     // or if the field is already at its character limit
84257                     var hideAdd = (optstrings && !available.length) || maxLength <= 0;
84258                     container.selectAll('.chiplist .input-wrap')
84259                         .style('display', hideAdd ? 'none' : null);
84260
84261
84262                     // Render chips
84263                     var chips = container.selectAll('.chip')
84264                         .data(_multiData);
84265
84266                     chips.exit()
84267                         .remove();
84268
84269                     var enter = chips.enter()
84270                         .insert('li', '.input-wrap')
84271                         .attr('class', 'chip');
84272
84273                     enter.append('span');
84274                     enter.append('a');
84275
84276                     chips = chips.merge(enter)
84277                         .order()
84278                         .classed('draggable', allowDragAndDrop)
84279                         .classed('mixed', function(d) {
84280                             return d.isMixed;
84281                         })
84282                         .attr('title', function(d) {
84283                             return d.isMixed ? _t('inspector.unshared_value_tooltip') : null;
84284                         });
84285
84286                     if (allowDragAndDrop) {
84287                         registerDragAndDrop(chips);
84288                     }
84289
84290                     chips.select('span')
84291                         .text(function(d) { return d.value; });
84292
84293                     chips.select('a')
84294                         .on('click', removeMultikey)
84295                         .attr('class', 'remove')
84296                         .text('×');
84297
84298                 } else {
84299                     var isMixed = Array.isArray(tags[field.key]);
84300
84301                     var mixedValues = isMixed && tags[field.key].map(function(val) {
84302                         return displayValue(val);
84303                     }).filter(Boolean);
84304
84305                     utilGetSetValue(input, !isMixed ? displayValue(tags[field.key]) : '')
84306                         .attr('title', isMixed ? mixedValues.join('\n') : undefined)
84307                         .attr('placeholder', isMixed ? _t('inspector.multiple_values') : _staticPlaceholder || '')
84308                         .classed('mixed', isMixed);
84309                 }
84310             };
84311
84312             function registerDragAndDrop(selection) {
84313
84314                 // allow drag and drop re-ordering of chips
84315                 var dragOrigin, targetIndex;
84316                 selection.call(d3_drag()
84317                     .on('start', function() {
84318                         dragOrigin = {
84319                             x: event.x,
84320                             y: event.y
84321                         };
84322                         targetIndex = null;
84323                     })
84324                     .on('drag', function(d, index) {
84325                         var x = event.x - dragOrigin.x,
84326                             y = event.y - dragOrigin.y;
84327
84328                         if (!select(this).classed('dragging') &&
84329                             // don't display drag until dragging beyond a distance threshold
84330                             Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)) <= 5) { return; }
84331
84332                         select(this)
84333                             .classed('dragging', true);
84334
84335                         targetIndex = null;
84336                         var targetIndexOffsetTop = null;
84337                         var draggedTagWidth = select(this).node().offsetWidth;
84338
84339                         if (field.key === 'destination') { // meaning tags are full width
84340                             container.selectAll('.chip')
84341                                 .style('transform', function(d2, index2) {
84342                                     var node = select(this).node();
84343
84344                                     if (index === index2) {
84345                                         return 'translate(' + x + 'px, ' + y + 'px)';
84346                                     // move the dragged tag up the order
84347                                     } else if (index2 > index && event.y > node.offsetTop) {
84348                                         if (targetIndex === null || index2 > targetIndex) {
84349                                             targetIndex = index2;
84350                                         }
84351                                         return 'translateY(-100%)';
84352                                     // move the dragged tag down the order
84353                                     } else if (index2 < index && event.y < node.offsetTop + node.offsetHeight) {
84354                                         if (targetIndex === null || index2 < targetIndex) {
84355                                             targetIndex = index2;
84356                                         }
84357                                         return 'translateY(100%)';
84358                                     }
84359                                     return null;
84360                                 });
84361                         } else {
84362                             container.selectAll('.chip')
84363                                 .each(function(d2, index2) {
84364                                     var node = select(this).node();
84365
84366                                     // check the cursor is in the bounding box
84367                                     if (
84368                                         index !== index2 &&
84369                                         event.x < node.offsetLeft + node.offsetWidth + 5 &&
84370                                         event.x > node.offsetLeft &&
84371                                         event.y < node.offsetTop + node.offsetHeight &&
84372                                         event.y > node.offsetTop
84373                                     ) {
84374                                         targetIndex = index2;
84375                                         targetIndexOffsetTop = node.offsetTop;
84376                                     }
84377                                 })
84378                                 .style('transform', function(d2, index2) {
84379                                     var node = select(this).node();
84380
84381                                     if (index === index2) {
84382                                         return 'translate(' + x + 'px, ' + y + 'px)';
84383                                     }
84384
84385                                     // only translate tags in the same row
84386                                     if (node.offsetTop === targetIndexOffsetTop) {
84387                                         if (index2 < index && index2 >= targetIndex) {
84388                                             return 'translateX(' + draggedTagWidth + 'px)';
84389                                         } else if (index2 > index && index2 <= targetIndex) {
84390                                             return 'translateX(-' + draggedTagWidth + 'px)';
84391                                         }
84392                                     }
84393                                     return null;
84394                                 });
84395                             }
84396                     })
84397                     .on('end', function(d, index) {
84398                         if (!select(this).classed('dragging')) {
84399                             return;
84400                         }
84401
84402                         select(this)
84403                             .classed('dragging', false);
84404
84405                         container.selectAll('.chip')
84406                             .style('transform', null);
84407
84408                         if (typeof targetIndex === 'number') {
84409                             var element = _multiData[index];
84410                             _multiData.splice(index, 1);
84411                             _multiData.splice(targetIndex, 0, element);
84412
84413                             var t = {};
84414
84415                             if (_multiData.length) {
84416                                 t[field.key] = _multiData.map(function(element) {
84417                                     return element.key;
84418                                 }).join(';');
84419                             } else {
84420                                 t[field.key] = undefined;
84421                             }
84422
84423                             dispatch$1.call('change', this, t);
84424                         }
84425                         dragOrigin = undefined;
84426                         targetIndex = undefined;
84427                     })
84428                 );
84429             }
84430
84431
84432             combo.focus = function() {
84433                 input.node().focus();
84434             };
84435
84436
84437             combo.entityIDs = function(val) {
84438                 if (!arguments.length) { return _entityIDs; }
84439                 _entityIDs = val;
84440                 return combo;
84441             };
84442
84443
84444             function combinedEntityExtent() {
84445                 return _entityIDs && _entityIDs.length && utilTotalExtent(_entityIDs, context.graph());
84446             }
84447
84448
84449             return utilRebind(combo, dispatch$1, 'on');
84450         }
84451
84452         function uiFieldText(field, context) {
84453             var dispatch$1 = dispatch('change');
84454             var input = select(null);
84455             var outlinkButton = select(null);
84456             var _entityIDs = [];
84457             var _tags;
84458             var _phoneFormats = {};
84459
84460             if (field.type === 'tel') {
84461                 _mainFileFetcher.get('phone_formats')
84462                     .then(function(d) {
84463                         _phoneFormats = d;
84464                         updatePhonePlaceholder();
84465                     })
84466                     .catch(function() { /* ignore */ });
84467             }
84468
84469             function i(selection) {
84470                 var entity = _entityIDs.length && context.hasEntity(_entityIDs[0]);
84471                 var preset = entity && _mainPresetIndex.match(entity, context.graph());
84472                 var isLocked = preset && preset.suggestion && field.id === 'brand';
84473                 field.locked(isLocked);
84474
84475                 var wrap = selection.selectAll('.form-field-input-wrap')
84476                     .data([0]);
84477
84478                 wrap = wrap.enter()
84479                     .append('div')
84480                     .attr('class', 'form-field-input-wrap form-field-input-' + field.type)
84481                     .merge(wrap);
84482
84483                 input = wrap.selectAll('input')
84484                     .data([0]);
84485
84486                 input = input.enter()
84487                     .append('input')
84488                     .attr('type', field.type === 'identifier' ? 'text' : field.type)
84489                     .attr('id', field.domId)
84490                     .classed(field.type, true)
84491                     .call(utilNoAuto)
84492                     .merge(input);
84493
84494                 input
84495                     .classed('disabled', !!isLocked)
84496                     .attr('readonly', isLocked || null)
84497                     .on('input', change(true))
84498                     .on('blur', change())
84499                     .on('change', change());
84500
84501
84502                 if (field.type === 'tel') {
84503                     updatePhonePlaceholder();
84504
84505                 } else if (field.type === 'number') {
84506                     var rtl = (_mainLocalizer.textDirection() === 'rtl');
84507
84508                     input.attr('type', 'text');
84509
84510                     var buttons = wrap.selectAll('.increment, .decrement')
84511                         .data(rtl ? [1, -1] : [-1, 1]);
84512
84513                     buttons.enter()
84514                         .append('button')
84515                         .attr('tabindex', -1)
84516                         .attr('class', function(d) {
84517                             var which = (d === 1 ? 'increment' : 'decrement');
84518                             return 'form-field-button ' + which;
84519                         })
84520                         .merge(buttons)
84521                         .on('click', function(d) {
84522                             event.preventDefault();
84523                             var raw_vals = input.node().value || '0';
84524                             var vals = raw_vals.split(';');
84525                             vals = vals.map(function(v) {
84526                                 var num = parseFloat(v.trim(), 10);
84527                                 return isFinite(num) ? clamped(num + d) : v.trim();
84528                             });
84529                             input.node().value = vals.join(';');
84530                             change()();
84531                         });
84532                 } else if (field.type === 'identifier' && field.urlFormat && field.pattern) {
84533
84534                     input.attr('type', 'text');
84535
84536                     outlinkButton = wrap.selectAll('.foreign-id-permalink')
84537                         .data([0]);
84538
84539                     outlinkButton.enter()
84540                         .append('button')
84541                         .attr('tabindex', -1)
84542                         .call(svgIcon('#iD-icon-out-link'))
84543                         .attr('class', 'form-field-button foreign-id-permalink')
84544                         .attr('title', function() {
84545                             var domainResults = /^https?:\/\/(.{1,}?)\//.exec(field.urlFormat);
84546                             if (domainResults.length >= 2 && domainResults[1]) {
84547                                 var domain = domainResults[1];
84548                                 return _t('icons.view_on', { domain: domain });
84549                             }
84550                             return '';
84551                         })
84552                         .on('click', function() {
84553                             event.preventDefault();
84554
84555                             var value = validIdentifierValueForLink();
84556                             if (value) {
84557                                 var url = field.urlFormat.replace(/{value}/, encodeURIComponent(value));
84558                                 window.open(url, '_blank');
84559                             }
84560                         })
84561                         .merge(outlinkButton);
84562                 }
84563             }
84564
84565
84566             function updatePhonePlaceholder() {
84567                 if (input.empty() || !Object.keys(_phoneFormats).length) { return; }
84568
84569                 var extent = combinedEntityExtent();
84570                 var countryCode = extent && iso1A2Code(extent.center());
84571                 var format = countryCode && _phoneFormats[countryCode.toLowerCase()];
84572                 if (format) { input.attr('placeholder', format); }
84573             }
84574
84575
84576             function validIdentifierValueForLink() {
84577                 if (field.type === 'identifier' && field.pattern) {
84578                     var value = utilGetSetValue(input).trim().split(';')[0];
84579                     return value && value.match(new RegExp(field.pattern));
84580                 }
84581                 return null;
84582             }
84583
84584
84585             // clamp number to min/max
84586             function clamped(num) {
84587                 if (field.minValue !== undefined) {
84588                     num = Math.max(num, field.minValue);
84589                 }
84590                 if (field.maxValue !== undefined) {
84591                     num = Math.min(num, field.maxValue);
84592                 }
84593                 return num;
84594             }
84595
84596
84597             function change(onInput) {
84598                 return function() {
84599                     var t = {};
84600                     var val = utilGetSetValue(input);
84601                     if (!onInput) { val = context.cleanTagValue(val); }
84602
84603                     // don't override multiple values with blank string
84604                     if (!val && Array.isArray(_tags[field.key])) { return; }
84605
84606                     if (!onInput) {
84607                         if (field.type === 'number' && val) {
84608                             var vals = val.split(';');
84609                             vals = vals.map(function(v) {
84610                                 var num = parseFloat(v.trim(), 10);
84611                                 return isFinite(num) ? clamped(num) : v.trim();
84612                             });
84613                             val = vals.join(';');
84614                         }
84615                         utilGetSetValue(input, val);
84616                     }
84617                     t[field.key] = val || undefined;
84618                     dispatch$1.call('change', this, t, onInput);
84619                 };
84620             }
84621
84622
84623             i.entityIDs = function(val) {
84624                 if (!arguments.length) { return _entityIDs; }
84625                 _entityIDs = val;
84626                 return i;
84627             };
84628
84629
84630             i.tags = function(tags) {
84631                 _tags = tags;
84632
84633                 var isMixed = Array.isArray(tags[field.key]);
84634
84635                 utilGetSetValue(input, !isMixed && tags[field.key] ? tags[field.key] : '')
84636                     .attr('title', isMixed ? tags[field.key].filter(Boolean).join('\n') : undefined)
84637                     .attr('placeholder', isMixed ? _t('inspector.multiple_values') : (field.placeholder() || _t('inspector.unknown')))
84638                     .classed('mixed', isMixed);
84639
84640                 if (outlinkButton && !outlinkButton.empty()) {
84641                     var disabled = !validIdentifierValueForLink();
84642                     outlinkButton.classed('disabled', disabled);
84643                 }
84644             };
84645
84646
84647             i.focus = function() {
84648                 var node = input.node();
84649                 if (node) { node.focus(); }
84650             };
84651
84652             function combinedEntityExtent() {
84653                 return _entityIDs && _entityIDs.length && utilTotalExtent(_entityIDs, context.graph());
84654             }
84655
84656             return utilRebind(i, dispatch$1, 'on');
84657         }
84658
84659         function uiFieldAccess(field, context) {
84660             var dispatch$1 = dispatch('change');
84661             var items = select(null);
84662             var _tags;
84663
84664             function access(selection) {
84665                 var wrap = selection.selectAll('.form-field-input-wrap')
84666                     .data([0]);
84667
84668                 wrap = wrap.enter()
84669                     .append('div')
84670                     .attr('class', 'form-field-input-wrap form-field-input-' + field.type)
84671                     .merge(wrap);
84672
84673                 var list = wrap.selectAll('ul')
84674                     .data([0]);
84675
84676                 list = list.enter()
84677                     .append('ul')
84678                     .attr('class', 'rows')
84679                     .merge(list);
84680
84681
84682                 items = list.selectAll('li')
84683                     .data(field.keys);
84684
84685                 // Enter
84686                 var enter = items.enter()
84687                     .append('li')
84688                     .attr('class', function(d) { return 'labeled-input preset-access-' + d; });
84689
84690                 enter
84691                     .append('span')
84692                     .attr('class', 'label preset-label-access')
84693                     .attr('for', function(d) { return 'preset-input-access-' + d; })
84694                     .text(function(d) { return field.t('types.' + d); });
84695
84696                 enter
84697                     .append('div')
84698                     .attr('class', 'preset-input-access-wrap')
84699                     .append('input')
84700                     .attr('type', 'text')
84701                     .attr('class', function(d) { return 'preset-input-access preset-input-access-' + d; })
84702                     .call(utilNoAuto)
84703                     .each(function(d) {
84704                         select(this)
84705                             .call(uiCombobox(context, 'access-' + d)
84706                                 .data(access.options(d))
84707                             );
84708                     });
84709
84710
84711                 // Update
84712                 items = items.merge(enter);
84713
84714                 wrap.selectAll('.preset-input-access')
84715                     .on('change', change)
84716                     .on('blur', change);
84717             }
84718
84719
84720             function change(d) {
84721                 var tag = {};
84722                 var value = context.cleanTagValue(utilGetSetValue(select(this)));
84723
84724                 // don't override multiple values with blank string
84725                 if (!value && typeof _tags[d] !== 'string') { return; }
84726
84727                 tag[d] = value || undefined;
84728                 dispatch$1.call('change', this, tag);
84729             }
84730
84731
84732             access.options = function(type) {
84733                 var options = ['no', 'permissive', 'private', 'permit', 'destination'];
84734
84735                 if (type !== 'access') {
84736                     options.unshift('yes');
84737                     options.push('designated');
84738
84739                     if (type === 'bicycle') {
84740                         options.push('dismount');
84741                     }
84742                 }
84743
84744                 return options.map(function(option) {
84745                     return {
84746                         title: field.t('options.' + option + '.description'),
84747                         value: option
84748                     };
84749                 });
84750             };
84751
84752
84753             var placeholdersByHighway = {
84754                 footway: {
84755                     foot: 'designated',
84756                     motor_vehicle: 'no'
84757                 },
84758                 steps: {
84759                     foot: 'yes',
84760                     motor_vehicle: 'no',
84761                     bicycle: 'no',
84762                     horse: 'no'
84763                 },
84764                 pedestrian: {
84765                     foot: 'yes',
84766                     motor_vehicle: 'no'
84767                 },
84768                 cycleway: {
84769                     motor_vehicle: 'no',
84770                     bicycle: 'designated'
84771                 },
84772                 bridleway: {
84773                     motor_vehicle: 'no',
84774                     horse: 'designated'
84775                 },
84776                 path: {
84777                     foot: 'yes',
84778                     motor_vehicle: 'no',
84779                     bicycle: 'yes',
84780                     horse: 'yes'
84781                 },
84782                 motorway: {
84783                     foot: 'no',
84784                     motor_vehicle: 'yes',
84785                     bicycle: 'no',
84786                     horse: 'no'
84787                 },
84788                 trunk: {
84789                     motor_vehicle: 'yes'
84790                 },
84791                 primary: {
84792                     foot: 'yes',
84793                     motor_vehicle: 'yes',
84794                     bicycle: 'yes',
84795                     horse: 'yes'
84796                 },
84797                 secondary: {
84798                     foot: 'yes',
84799                     motor_vehicle: 'yes',
84800                     bicycle: 'yes',
84801                     horse: 'yes'
84802                 },
84803                 tertiary: {
84804                     foot: 'yes',
84805                     motor_vehicle: 'yes',
84806                     bicycle: 'yes',
84807                     horse: 'yes'
84808                 },
84809                 residential: {
84810                     foot: 'yes',
84811                     motor_vehicle: 'yes',
84812                     bicycle: 'yes',
84813                     horse: 'yes'
84814                 },
84815                 unclassified: {
84816                     foot: 'yes',
84817                     motor_vehicle: 'yes',
84818                     bicycle: 'yes',
84819                     horse: 'yes'
84820                 },
84821                 service: {
84822                     foot: 'yes',
84823                     motor_vehicle: 'yes',
84824                     bicycle: 'yes',
84825                     horse: 'yes'
84826                 },
84827                 motorway_link: {
84828                     foot: 'no',
84829                     motor_vehicle: 'yes',
84830                     bicycle: 'no',
84831                     horse: 'no'
84832                 },
84833                 trunk_link: {
84834                     motor_vehicle: 'yes'
84835                 },
84836                 primary_link: {
84837                     foot: 'yes',
84838                     motor_vehicle: 'yes',
84839                     bicycle: 'yes',
84840                     horse: 'yes'
84841                 },
84842                 secondary_link: {
84843                     foot: 'yes',
84844                     motor_vehicle: 'yes',
84845                     bicycle: 'yes',
84846                     horse: 'yes'
84847                 },
84848                 tertiary_link: {
84849                     foot: 'yes',
84850                     motor_vehicle: 'yes',
84851                     bicycle: 'yes',
84852                     horse: 'yes'
84853                 }
84854             };
84855
84856
84857             access.tags = function(tags) {
84858                 _tags = tags;
84859
84860                 utilGetSetValue(items.selectAll('.preset-input-access'), function(d) {
84861                         return typeof tags[d] === 'string' ? tags[d] : '';
84862                     })
84863                     .classed('mixed', function(d) {
84864                         return tags[d] && Array.isArray(tags[d]);
84865                     })
84866                     .attr('title', function(d) {
84867                         return tags[d] && Array.isArray(tags[d]) && tags[d].filter(Boolean).join('\n');
84868                     })
84869                     .attr('placeholder', function(d) {
84870                         if (tags[d] && Array.isArray(tags[d])) {
84871                             return _t('inspector.multiple_values');
84872                         }
84873                         if (d === 'access') {
84874                             return 'yes';
84875                         }
84876                         if (tags.access && typeof tags.access === 'string') {
84877                             return tags.access;
84878                         }
84879                         if (tags.highway) {
84880                             if (typeof tags.highway === 'string') {
84881                                 if (placeholdersByHighway[tags.highway] &&
84882                                     placeholdersByHighway[tags.highway][d]) {
84883
84884                                     return placeholdersByHighway[tags.highway][d];
84885                                 }
84886                             } else {
84887                                 var impliedAccesses = tags.highway.filter(Boolean).map(function(highwayVal) {
84888                                     return placeholdersByHighway[highwayVal] && placeholdersByHighway[highwayVal][d];
84889                                 }).filter(Boolean);
84890
84891                                 if (impliedAccesses.length === tags.highway.length &&
84892                                     new Set(impliedAccesses).size === 1) {
84893                                     // if all the highway values have the same implied access for this type then use that
84894                                     return impliedAccesses[0];
84895                                 }
84896                             }
84897                         }
84898                         return field.placeholder();
84899                     });
84900             };
84901
84902
84903             access.focus = function() {
84904                 items.selectAll('.preset-input-access')
84905                     .node().focus();
84906             };
84907
84908
84909             return utilRebind(access, dispatch$1, 'on');
84910         }
84911
84912         function uiFieldAddress(field, context) {
84913             var dispatch$1 = dispatch('change');
84914             var _selection = select(null);
84915             var _wrap = select(null);
84916             var addrField = _mainPresetIndex.field('address');   // needed for placeholder strings
84917
84918             var _entityIDs = [];
84919             var _tags;
84920             var _countryCode;
84921             var _addressFormats = [{
84922                 format: [
84923                     ['housenumber', 'street'],
84924                     ['city', 'postcode']
84925                 ]
84926               }];
84927
84928             _mainFileFetcher.get('address_formats')
84929                 .then(function(d) {
84930                     _addressFormats = d;
84931                     if (!_selection.empty()) {
84932                         _selection.call(address);
84933                     }
84934                 })
84935                 .catch(function() { /* ignore */ });
84936
84937
84938             function getNearStreets() {
84939                 var extent = combinedEntityExtent();
84940                 var l = extent.center();
84941                 var box = geoExtent(l).padByMeters(200);
84942
84943                 var streets = context.history().intersects(box)
84944                     .filter(isAddressable)
84945                     .map(function(d) {
84946                         var loc = context.projection([
84947                             (extent[0][0] + extent[1][0]) / 2,
84948                             (extent[0][1] + extent[1][1]) / 2
84949                         ]);
84950                         var choice = geoChooseEdge(context.graph().childNodes(d), loc, context.projection);
84951
84952                         return {
84953                             title: d.tags.name,
84954                             value: d.tags.name,
84955                             dist: choice.distance
84956                         };
84957                     })
84958                     .sort(function(a, b) {
84959                         return a.dist - b.dist;
84960                     });
84961
84962                 return utilArrayUniqBy(streets, 'value');
84963
84964                 function isAddressable(d) {
84965                     return d.tags.highway && d.tags.name && d.type === 'way';
84966                 }
84967             }
84968
84969
84970             function getNearCities() {
84971                 var extent = combinedEntityExtent();
84972                 var l = extent.center();
84973                 var box = geoExtent(l).padByMeters(200);
84974
84975                 var cities = context.history().intersects(box)
84976                     .filter(isAddressable)
84977                     .map(function(d) {
84978                         return {
84979                             title: d.tags['addr:city'] || d.tags.name,
84980                             value: d.tags['addr:city'] || d.tags.name,
84981                             dist: geoSphericalDistance(d.extent(context.graph()).center(), l)
84982                         };
84983                     })
84984                     .sort(function(a, b) {
84985                         return a.dist - b.dist;
84986                     });
84987
84988                 return utilArrayUniqBy(cities, 'value');
84989
84990
84991                 function isAddressable(d) {
84992                     if (d.tags.name) {
84993                         if (d.tags.admin_level === '8' && d.tags.boundary === 'administrative')
84994                             { return true; }
84995                         if (d.tags.border_type === 'city')
84996                             { return true; }
84997                         if (d.tags.place === 'city' || d.tags.place === 'town' || d.tags.place === 'village')
84998                             { return true; }
84999                     }
85000
85001                     if (d.tags['addr:city'])
85002                         { return true; }
85003
85004                     return false;
85005                 }
85006             }
85007
85008             function getNearValues(key) {
85009                 var extent = combinedEntityExtent();
85010                 var l = extent.center();
85011                 var box = geoExtent(l).padByMeters(200);
85012
85013                 var results = context.history().intersects(box)
85014                     .filter(function hasTag(d) { return _entityIDs.indexOf(d.id) === -1 && d.tags[key]; })
85015                     .map(function(d) {
85016                         return {
85017                             title: d.tags[key],
85018                             value: d.tags[key],
85019                             dist: geoSphericalDistance(d.extent(context.graph()).center(), l)
85020                         };
85021                     })
85022                     .sort(function(a, b) {
85023                         return a.dist - b.dist;
85024                     });
85025
85026                 return utilArrayUniqBy(results, 'value');
85027             }
85028
85029
85030             function updateForCountryCode() {
85031
85032                 if (!_countryCode) { return; }
85033
85034                 var addressFormat;
85035                 for (var i = 0; i < _addressFormats.length; i++) {
85036                     var format = _addressFormats[i];
85037                     if (!format.countryCodes) {
85038                         addressFormat = format;   // choose the default format, keep going
85039                     } else if (format.countryCodes.indexOf(_countryCode) !== -1) {
85040                         addressFormat = format;   // choose the country format, stop here
85041                         break;
85042                     }
85043                 }
85044
85045                 var dropdowns = addressFormat.dropdowns || [
85046                     'city', 'county', 'country', 'district', 'hamlet',
85047                     'neighbourhood', 'place', 'postcode', 'province',
85048                     'quarter', 'state', 'street', 'subdistrict', 'suburb'
85049                 ];
85050
85051                 var widths = addressFormat.widths || {
85052                     housenumber: 1/3, street: 2/3,
85053                     city: 2/3, state: 1/4, postcode: 1/3
85054                 };
85055
85056                 function row(r) {
85057                     // Normalize widths.
85058                     var total = r.reduce(function(sum, key) {
85059                         return sum + (widths[key] || 0.5);
85060                     }, 0);
85061
85062                     return r.map(function(key) {
85063                         return {
85064                             id: key,
85065                             width: (widths[key] || 0.5) / total
85066                         };
85067                     });
85068                 }
85069
85070                 var rows = _wrap.selectAll('.addr-row')
85071                     .data(addressFormat.format, function(d) {
85072                         return d.toString();
85073                     });
85074
85075                 rows.exit()
85076                     .remove();
85077
85078                 rows
85079                     .enter()
85080                     .append('div')
85081                     .attr('class', 'addr-row')
85082                     .selectAll('input')
85083                     .data(row)
85084                     .enter()
85085                     .append('input')
85086                     .property('type', 'text')
85087                     .call(updatePlaceholder)
85088                     .attr('class', function (d) { return 'addr-' + d.id; })
85089                     .call(utilNoAuto)
85090                     .each(addDropdown)
85091                     .style('width', function (d) { return d.width * 100 + '%'; });
85092
85093
85094                 function addDropdown(d) {
85095                     if (dropdowns.indexOf(d.id) === -1) { return; }  // not a dropdown
85096
85097                     var nearValues = (d.id === 'street') ? getNearStreets
85098                         : (d.id === 'city') ? getNearCities
85099                         : getNearValues;
85100
85101                     select(this)
85102                         .call(uiCombobox(context, 'address-' + d.id)
85103                             .minItems(1)
85104                             .caseSensitive(true)
85105                             .fetcher(function(value, callback) {
85106                                 callback(nearValues('addr:' + d.id));
85107                             })
85108                         );
85109                 }
85110
85111                 _wrap.selectAll('input')
85112                     .on('blur', change())
85113                     .on('change', change());
85114
85115                 _wrap.selectAll('input:not(.combobox-input)')
85116                     .on('input', change(true));
85117
85118                 if (_tags) { updateTags(_tags); }
85119             }
85120
85121
85122             function address(selection) {
85123                 _selection = selection;
85124
85125                 _wrap = selection.selectAll('.form-field-input-wrap')
85126                     .data([0]);
85127
85128                 _wrap = _wrap.enter()
85129                     .append('div')
85130                     .attr('class', 'form-field-input-wrap form-field-input-' + field.type)
85131                     .merge(_wrap);
85132
85133                 var extent = combinedEntityExtent();
85134
85135                 if (extent) {
85136                     var countryCode;
85137                     if (context.inIntro()) {
85138                         // localize the address format for the walkthrough
85139                         countryCode = _t('intro.graph.countrycode');
85140                     } else {
85141                         var center = extent.center();
85142                         countryCode = iso1A2Code(center);
85143                     }
85144                     if (countryCode) {
85145                         _countryCode = countryCode.toLowerCase();
85146                         updateForCountryCode();
85147                     }
85148                 }
85149             }
85150
85151
85152             function change(onInput) {
85153                 return function() {
85154                     var tags = {};
85155
85156                     _wrap.selectAll('input')
85157                         .each(function (subfield) {
85158                             var key = field.key + ':' + subfield.id;
85159
85160                             var value = this.value;
85161                             if (!onInput) { value = context.cleanTagValue(value); }
85162
85163                             // don't override multiple values with blank string
85164                             if (Array.isArray(_tags[key]) && !value) { return; }
85165
85166                             tags[key] = value || undefined;
85167                         });
85168
85169                     dispatch$1.call('change', this, tags, onInput);
85170                 };
85171             }
85172
85173             function updatePlaceholder(inputSelection) {
85174                 return inputSelection.attr('placeholder', function(subfield) {
85175                     if (_tags && Array.isArray(_tags[field.key + ':' + subfield.id])) {
85176                         return _t('inspector.multiple_values');
85177                     }
85178                     if (_countryCode) {
85179                         var localkey = subfield.id + '!' + _countryCode;
85180                         var tkey = addrField.strings.placeholders[localkey] ? localkey : subfield.id;
85181                         return addrField.t('placeholders.' + tkey);
85182                     }
85183                 });
85184             }
85185
85186
85187             function updateTags(tags) {
85188                 utilGetSetValue(_wrap.selectAll('input'), function (subfield) {
85189                         var val = tags[field.key + ':' + subfield.id];
85190                         return typeof val === 'string' ? val : '';
85191                     })
85192                     .attr('title', function(subfield) {
85193                         var val = tags[field.key + ':' + subfield.id];
85194                         return val && Array.isArray(val) && val.filter(Boolean).join('\n');
85195                     })
85196                     .classed('mixed', function(subfield) {
85197                         return Array.isArray(tags[field.key + ':' + subfield.id]);
85198                     })
85199                     .call(updatePlaceholder);
85200             }
85201
85202
85203             function combinedEntityExtent() {
85204                 return _entityIDs && _entityIDs.length && utilTotalExtent(_entityIDs, context.graph());
85205             }
85206
85207
85208             address.entityIDs = function(val) {
85209                 if (!arguments.length) { return _entityIDs; }
85210                 _entityIDs = val;
85211                 return address;
85212             };
85213
85214
85215             address.tags = function(tags) {
85216                 _tags = tags;
85217                 updateTags(tags);
85218             };
85219
85220
85221             address.focus = function() {
85222                 var node = _wrap.selectAll('input').node();
85223                 if (node) { node.focus(); }
85224             };
85225
85226
85227             return utilRebind(address, dispatch$1, 'on');
85228         }
85229
85230         function uiFieldCycleway(field, context) {
85231             var dispatch$1 = dispatch('change');
85232             var items = select(null);
85233             var wrap = select(null);
85234             var _tags;
85235
85236             function cycleway(selection) {
85237
85238                 function stripcolon(s) {
85239                     return s.replace(':', '');
85240                 }
85241
85242
85243                 wrap = selection.selectAll('.form-field-input-wrap')
85244                     .data([0]);
85245
85246                 wrap = wrap.enter()
85247                     .append('div')
85248                     .attr('class', 'form-field-input-wrap form-field-input-' + field.type)
85249                     .merge(wrap);
85250
85251
85252                 var div = wrap.selectAll('ul')
85253                     .data([0]);
85254
85255                 div = div.enter()
85256                     .append('ul')
85257                     .attr('class', 'rows')
85258                     .merge(div);
85259
85260                 var keys = ['cycleway:left', 'cycleway:right'];
85261
85262                 items = div.selectAll('li')
85263                     .data(keys);
85264
85265                 var enter = items.enter()
85266                     .append('li')
85267                     .attr('class', function(d) { return 'labeled-input preset-cycleway-' + stripcolon(d); });
85268
85269                 enter
85270                     .append('span')
85271                     .attr('class', 'label preset-label-cycleway')
85272                     .attr('for', function(d) { return 'preset-input-cycleway-' + stripcolon(d); })
85273                     .text(function(d) { return field.t('types.' + d); });
85274
85275                 enter
85276                     .append('div')
85277                     .attr('class', 'preset-input-cycleway-wrap')
85278                     .append('input')
85279                     .attr('type', 'text')
85280                     .attr('class', function(d) { return 'preset-input-cycleway preset-input-' + stripcolon(d); })
85281                     .call(utilNoAuto)
85282                     .each(function(d) {
85283                         select(this)
85284                             .call(uiCombobox(context, 'cycleway-' + stripcolon(d))
85285                                 .data(cycleway.options(d))
85286                             );
85287                     });
85288
85289                 items = items.merge(enter);
85290
85291                 // Update
85292                 wrap.selectAll('.preset-input-cycleway')
85293                     .on('change', change)
85294                     .on('blur', change);
85295             }
85296
85297
85298             function change(key) {
85299
85300                 var newValue = context.cleanTagValue(utilGetSetValue(select(this)));
85301
85302                 // don't override multiple values with blank string
85303                 if (!newValue && (Array.isArray(_tags.cycleway) || Array.isArray(_tags[key]))) { return; }
85304
85305                 if (newValue === 'none' || newValue === '') { newValue = undefined; }
85306
85307                 var otherKey = key === 'cycleway:left' ? 'cycleway:right' : 'cycleway:left';
85308                 var otherValue = typeof _tags.cycleway === 'string' ? _tags.cycleway : _tags[otherKey];
85309                 if (otherValue && Array.isArray(otherValue)) {
85310                     // we must always have an explicit value for comparison
85311                     otherValue = otherValue[0];
85312                 }
85313                 if (otherValue === 'none' || otherValue === '') { otherValue = undefined; }
85314
85315                 var tag = {};
85316
85317                 // If the left and right tags match, use the cycleway tag to tag both
85318                 // sides the same way
85319                 if (newValue === otherValue) {
85320                     tag = {
85321                         cycleway: newValue,
85322                         'cycleway:left': undefined,
85323                         'cycleway:right': undefined
85324                     };
85325                 } else {
85326                     // Always set both left and right as changing one can affect the other
85327                     tag = {
85328                         cycleway: undefined
85329                     };
85330                     tag[key] = newValue;
85331                     tag[otherKey] = otherValue;
85332                 }
85333
85334                 dispatch$1.call('change', this, tag);
85335             }
85336
85337
85338             cycleway.options = function() {
85339                 return Object.keys(field.strings.options).map(function(option) {
85340                     return {
85341                         title: field.t('options.' + option + '.description'),
85342                         value: option
85343                     };
85344                 });
85345             };
85346
85347
85348             cycleway.tags = function(tags) {
85349                 _tags = tags;
85350
85351                 // If cycleway is set, use that instead of individual values
85352                 var commonValue = typeof tags.cycleway === 'string' && tags.cycleway;
85353
85354                 utilGetSetValue(items.selectAll('.preset-input-cycleway'), function(d) {
85355                         if (commonValue) { return commonValue; }
85356                         return !tags.cycleway && typeof tags[d] === 'string' ? tags[d] : '';
85357                     })
85358                     .attr('title', function(d) {
85359                         if (Array.isArray(tags.cycleway) || Array.isArray(tags[d])) {
85360                             var vals = [];
85361                             if (Array.isArray(tags.cycleway)) {
85362                                 vals = vals.concat(tags.cycleway);
85363                             }
85364                             if (Array.isArray(tags[d])) {
85365                                 vals = vals.concat(tags[d]);
85366                             }
85367                             return vals.filter(Boolean).join('\n');
85368                         }
85369                         return null;
85370                     })
85371                     .attr('placeholder', function(d) {
85372                         if (Array.isArray(tags.cycleway) || Array.isArray(tags[d])) {
85373                             return _t('inspector.multiple_values');
85374                         }
85375                         return field.placeholder();
85376                     })
85377                     .classed('mixed', function(d) {
85378                         return Array.isArray(tags.cycleway) || Array.isArray(tags[d]);
85379                     });
85380             };
85381
85382
85383             cycleway.focus = function() {
85384                 var node = wrap.selectAll('input').node();
85385                 if (node) { node.focus(); }
85386             };
85387
85388
85389             return utilRebind(cycleway, dispatch$1, 'on');
85390         }
85391
85392         function uiFieldLanes(field, context) {
85393             var dispatch$1 = dispatch('change');
85394             var LANE_WIDTH = 40;
85395             var LANE_HEIGHT = 200;
85396             var _entityIDs = [];
85397
85398             function lanes(selection) {
85399                 var lanesData = context.entity(_entityIDs[0]).lanes();
85400
85401                 if (!context.container().select('.inspector-wrap.inspector-hidden').empty() || !selection.node().parentNode) {
85402                     selection.call(lanes.off);
85403                     return;
85404                 }
85405
85406                 var wrap = selection.selectAll('.form-field-input-wrap')
85407                     .data([0]);
85408
85409                 wrap = wrap.enter()
85410                     .append('div')
85411                     .attr('class', 'form-field-input-wrap form-field-input-' + field.type)
85412                     .merge(wrap);
85413
85414                 var surface =  wrap.selectAll('.surface')
85415                     .data([0]);
85416
85417                 var d = utilGetDimensions(wrap);
85418                 var freeSpace = d[0] - lanesData.lanes.length * LANE_WIDTH * 1.5 + LANE_WIDTH * 0.5;
85419
85420                 surface = surface.enter()
85421                     .append('svg')
85422                     .attr('width', d[0])
85423                     .attr('height', 300)
85424                     .attr('class', 'surface')
85425                     .merge(surface);
85426
85427
85428                 var lanesSelection = surface.selectAll('.lanes')
85429                     .data([0]);
85430
85431                 lanesSelection = lanesSelection.enter()
85432                     .append('g')
85433                     .attr('class', 'lanes')
85434                     .merge(lanesSelection);
85435
85436                 lanesSelection
85437                     .attr('transform', function () {
85438                         return 'translate(' + (freeSpace / 2) + ', 0)';
85439                     });
85440
85441
85442                 var lane = lanesSelection.selectAll('.lane')
85443                    .data(lanesData.lanes);
85444
85445                 lane.exit()
85446                     .remove();
85447
85448                 var enter = lane.enter()
85449                     .append('g')
85450                     .attr('class', 'lane');
85451
85452                 enter
85453                     .append('g')
85454                     .append('rect')
85455                     .attr('y', 50)
85456                     .attr('width', LANE_WIDTH)
85457                     .attr('height', LANE_HEIGHT);
85458
85459                 enter
85460                     .append('g')
85461                     .attr('class', 'forward')
85462                     .append('text')
85463                     .attr('y', 40)
85464                     .attr('x', 14)
85465                     .text('▲');
85466
85467                 enter
85468                     .append('g')
85469                     .attr('class', 'bothways')
85470                     .append('text')
85471                     .attr('y', 40)
85472                     .attr('x', 14)
85473                     .text('▲▼');
85474
85475                 enter
85476                     .append('g')
85477                     .attr('class', 'backward')
85478                     .append('text')
85479                     .attr('y', 40)
85480                     .attr('x', 14)
85481                     .text('▼');
85482
85483
85484                 lane = lane
85485                     .merge(enter);
85486
85487                 lane
85488                     .attr('transform', function(d) {
85489                         return 'translate(' + (LANE_WIDTH * d.index * 1.5) + ', 0)';
85490                     });
85491
85492                 lane.select('.forward')
85493                     .style('visibility', function(d) {
85494                         return d.direction === 'forward' ? 'visible' : 'hidden';
85495                     });
85496
85497                 lane.select('.bothways')
85498                     .style('visibility', function(d) {
85499                         return d.direction === 'bothways' ? 'visible' : 'hidden';
85500                     });
85501
85502                 lane.select('.backward')
85503                     .style('visibility', function(d) {
85504                         return d.direction === 'backward' ? 'visible' : 'hidden';
85505                     });
85506             }
85507
85508
85509             lanes.entityIDs = function(val) {
85510                 _entityIDs = val;
85511             };
85512
85513             lanes.tags = function() {};
85514             lanes.focus = function() {};
85515             lanes.off = function() {};
85516
85517             return utilRebind(lanes, dispatch$1, 'on');
85518         }
85519
85520         uiFieldLanes.supportsMultiselection = false;
85521
85522         var _languagesArray = [];
85523
85524
85525         function uiFieldLocalized(field, context) {
85526             var dispatch$1 = dispatch('change', 'input');
85527             var wikipedia = services.wikipedia;
85528             var input = select(null);
85529             var localizedInputs = select(null);
85530             var _countryCode;
85531             var _tags;
85532
85533
85534             // A concern here in switching to async data means that _languagesArray will not
85535             // be available the first time through, so things like the fetchers and
85536             // the language() function will not work immediately.
85537             _mainFileFetcher.get('languages')
85538                 .then(loadLanguagesArray)
85539                 .catch(function() { /* ignore */ });
85540
85541             var _territoryLanguages = {};
85542             _mainFileFetcher.get('territory_languages')
85543                 .then(function(d) { _territoryLanguages = d; })
85544                 .catch(function() { /* ignore */ });
85545
85546
85547             var allSuggestions = _mainPresetIndex.collection.filter(function(p) {
85548                 return p.suggestion === true;
85549             });
85550
85551             // reuse these combos
85552             var langCombo = uiCombobox(context, 'localized-lang')
85553                 .fetcher(fetchLanguages)
85554                 .minItems(0);
85555
85556             var brandCombo = uiCombobox(context, 'localized-brand')
85557                 .canAutocomplete(false)
85558                 .minItems(1);
85559
85560             var _selection = select(null);
85561             var _multilingual = [];
85562             var _buttonTip = uiTooltip()
85563                 .title(_t('translate.translate'))
85564                 .placement('left');
85565             var _wikiTitles;
85566             var _entityIDs = [];
85567
85568
85569             function loadLanguagesArray(dataLanguages) {
85570                 if (_languagesArray.length !== 0) { return; }
85571
85572                 // some conversion is needed to ensure correct OSM tags are used
85573                 var replacements = {
85574                     sr: 'sr-Cyrl',      // in OSM, `sr` implies Cyrillic
85575                     'sr-Cyrl': false    // `sr-Cyrl` isn't used in OSM
85576                 };
85577
85578                 for (var code in dataLanguages) {
85579                     if (replacements[code] === false) { continue; }
85580                     var metaCode = code;
85581                     if (replacements[code]) { metaCode = replacements[code]; }
85582
85583                     _languagesArray.push({
85584                         localName: _mainLocalizer.languageName(metaCode, { localOnly: true }),
85585                         nativeName: dataLanguages[metaCode].nativeName,
85586                         code: code,
85587                         label: _mainLocalizer.languageName(metaCode)
85588                     });
85589                 }
85590             }
85591
85592
85593             function calcLocked() {
85594
85595                 // only lock the Name field
85596                 var isLocked = field.id === 'name' &&
85597                     _entityIDs.length &&
85598                     // lock the field if any feature needs it
85599                     _entityIDs.some(function(entityID) {
85600
85601                         var entity = context.graph().hasEntity(entityID);
85602                         if (!entity) { return false; }
85603
85604                         var original = context.graph().base().entities[_entityIDs[0]];
85605                         var hasOriginalName = original && entity.tags.name && entity.tags.name === original.tags.name;
85606                         // if the name was already edited manually then allow further editing
85607                         if (!hasOriginalName) { return false; }
85608
85609                         // features linked to Wikidata are likely important and should be protected
85610                         if (entity.tags.wikidata) { return true; }
85611
85612                         // assume the name has already been confirmed if its source has been researched
85613                         if (entity.tags['name:etymology:wikidata']) { return true; }
85614
85615                         var preset = _mainPresetIndex.match(entity, context.graph());
85616                         var isSuggestion = preset && preset.suggestion;
85617                         var showsBrand = preset && preset.originalFields.filter(function(d) {
85618                             return d.id === 'brand';
85619                         }).length;
85620                         // protect standardized brand names
85621                         return isSuggestion && !showsBrand;
85622                     });
85623
85624                 field.locked(isLocked);
85625             }
85626
85627
85628             // update _multilingual, maintaining the existing order
85629             function calcMultilingual(tags) {
85630                 var existingLangsOrdered = _multilingual.map(function(item) {
85631                     return item.lang;
85632                 });
85633                 var existingLangs = new Set(existingLangsOrdered.filter(Boolean));
85634
85635                 for (var k in tags) {
85636                     var m = k.match(/^(.*):([a-zA-Z_-]+)$/);
85637                     if (m && m[1] === field.key && m[2]) {
85638                         var item = { lang: m[2], value: tags[k] };
85639                         if (existingLangs.has(item.lang)) {
85640                             // update the value
85641                             _multilingual[existingLangsOrdered.indexOf(item.lang)].value = item.value;
85642                             existingLangs.delete(item.lang);
85643                         } else {
85644                             _multilingual.push(item);
85645                         }
85646                     }
85647                 }
85648
85649                 _multilingual = _multilingual.filter(function(item) {
85650                     return !item.lang || !existingLangs.has(item.lang);
85651                 });
85652             }
85653
85654
85655             function localized(selection) {
85656                 _selection = selection;
85657                 calcLocked();
85658                 var isLocked = field.locked();
85659                 var singularEntity = _entityIDs.length === 1 && context.hasEntity(_entityIDs[0]);
85660                 var preset = singularEntity && _mainPresetIndex.match(singularEntity, context.graph());
85661
85662                 var wrap = selection.selectAll('.form-field-input-wrap')
85663                     .data([0]);
85664
85665                 // enter/update
85666                 wrap = wrap.enter()
85667                     .append('div')
85668                     .attr('class', 'form-field-input-wrap form-field-input-' + field.type)
85669                     .merge(wrap);
85670
85671                 input = wrap.selectAll('.localized-main')
85672                     .data([0]);
85673
85674                 // enter/update
85675                 input = input.enter()
85676                     .append('input')
85677                     .attr('type', 'text')
85678                     .attr('id', field.domId)
85679                     .attr('class', 'localized-main')
85680                     .call(utilNoAuto)
85681                     .merge(input);
85682
85683                 if (preset && field.id === 'name') {
85684                     var pTag = preset.id.split('/', 2);
85685                     var pKey = pTag[0];
85686                     var pValue = pTag[1];
85687
85688                     if (!preset.suggestion) {
85689                         // Not a suggestion preset - Add a suggestions dropdown if it makes sense to.
85690                         // This code attempts to determine if the matched preset is the
85691                         // kind of preset that even can benefit from name suggestions..
85692                         // - true = shops, cafes, hotels, etc. (also generic and fallback presets)
85693                         // - false = churches, parks, hospitals, etc. (things not in the index)
85694                         var isFallback = preset.isFallback();
85695                         var goodSuggestions = allSuggestions.filter(function(s) {
85696                             if (isFallback) { return true; }
85697                             var sTag = s.id.split('/', 2);
85698                             var sKey = sTag[0];
85699                             var sValue = sTag[1];
85700                             return pKey === sKey && (!pValue || pValue === sValue);
85701                         });
85702
85703                         // Show the suggestions.. If the user picks one, change the tags..
85704                         if (allSuggestions.length && goodSuggestions.length) {
85705                             input
85706                                 .on('blur.localized', checkBrandOnBlur)
85707                                 .call(brandCombo
85708                                     .fetcher(fetchBrandNames(preset, allSuggestions))
85709                                     .on('accept', acceptBrand)
85710                                     .on('cancel', cancelBrand)
85711                                 );
85712                         }
85713                     }
85714                 }
85715
85716                 input
85717                     .classed('disabled', !!isLocked)
85718                     .attr('readonly', isLocked || null)
85719                     .on('input', change(true))
85720                     .on('blur', change())
85721                     .on('change', change());
85722
85723
85724                 var translateButton = wrap.selectAll('.localized-add')
85725                     .data([0]);
85726
85727                 translateButton = translateButton.enter()
85728                     .append('button')
85729                     .attr('class', 'localized-add form-field-button')
85730                     .attr('tabindex', -1)
85731                     .call(svgIcon('#iD-icon-plus'))
85732                     .merge(translateButton);
85733
85734                 translateButton
85735                     .classed('disabled', !!isLocked)
85736                     .call(isLocked ? _buttonTip.destroy : _buttonTip)
85737                     .on('click', addNew);
85738
85739
85740                 if (_tags && !_multilingual.length) {
85741                     calcMultilingual(_tags);
85742                 }
85743
85744                 localizedInputs = selection.selectAll('.localized-multilingual')
85745                     .data([0]);
85746
85747                 localizedInputs = localizedInputs.enter()
85748                     .append('div')
85749                     .attr('class', 'localized-multilingual')
85750                     .merge(localizedInputs);
85751
85752                 localizedInputs
85753                     .call(renderMultilingual);
85754
85755                 localizedInputs.selectAll('button, input')
85756                     .classed('disabled', !!isLocked)
85757                     .attr('readonly', isLocked || null);
85758
85759
85760
85761                 // We are not guaranteed to get an `accept` or `cancel` when blurring the field.
85762                 // (This can happen if the user actives the combo, arrows down, and then clicks off to blur)
85763                 // So compare the current field value against the suggestions one last time.
85764                 function checkBrandOnBlur() {
85765                     var latest = _entityIDs.length === 1 && context.hasEntity(_entityIDs[0]);
85766                     if (!latest) { return; }   // deleting the entity blurred the field?
85767
85768                     var preset = _mainPresetIndex.match(latest, context.graph());
85769                     if (preset && preset.suggestion) { return; }   // already accepted
85770
85771                     // note: here we are testing against "decorated" names, i.e. 'Starbucks – Cafe'
85772                     var name = utilGetSetValue(input).trim();
85773                     var matched = allSuggestions.filter(function(s) { return name === s.name(); });
85774
85775                     if (matched.length === 1) {
85776                         acceptBrand({ suggestion: matched[0] });
85777                     } else {
85778                         cancelBrand();
85779                     }
85780                 }
85781
85782
85783                 function acceptBrand(d) {
85784
85785                     var entity = _entityIDs.length === 1 && context.hasEntity(_entityIDs[0]);
85786
85787                     if (!d || !entity) {
85788                         cancelBrand();
85789                         return;
85790                     }
85791
85792                     var tags = entity.tags;
85793                     var geometry = entity.geometry(context.graph());
85794                     var removed = preset.unsetTags(tags, geometry);
85795                     for (var k in tags) {
85796                         tags[k] = removed[k];  // set removed tags to `undefined`
85797                     }
85798                     tags = d.suggestion.setTags(tags, geometry);
85799                     utilGetSetValue(input, tags.name);
85800                     dispatch$1.call('change', this, tags);
85801                 }
85802
85803
85804                 // user hit escape, clean whatever preset name appears after the last ' – '
85805                 function cancelBrand() {
85806                     var name = utilGetSetValue(input);
85807                     var clean = cleanName(name);
85808                     if (clean !== name) {
85809                         utilGetSetValue(input, clean);
85810                         dispatch$1.call('change', this, { name: clean });
85811                     }
85812                 }
85813
85814                 // Remove whatever is after the last ' – '
85815                 // NOTE: split/join on en-dash, not a hyphen (to avoid conflict with fr - nl names in Brussels etc)
85816                 function cleanName(name) {
85817                     var parts = name.split(' – ');
85818                     if (parts.length > 1) {
85819                         parts.pop();
85820                         name = parts.join(' – ');
85821                     }
85822                     return name;
85823                 }
85824
85825
85826                 function fetchBrandNames(preset, suggestions) {
85827                     var pTag = preset.id.split('/', 2);
85828                     var pKey = pTag[0];
85829                     var pValue = pTag[1];
85830
85831                     return function(value, callback) {
85832                         var results = [];
85833                         if (value && value.length > 2) {
85834                             for (var i = 0; i < suggestions.length; i++) {
85835                                 var s = suggestions[i];
85836
85837                                 // don't suggest brands from incompatible countries
85838                                 if (_countryCode && s.countryCodes &&
85839                                     s.countryCodes.indexOf(_countryCode) === -1) { continue; }
85840
85841                                 var sTag = s.id.split('/', 2);
85842                                 var sKey = sTag[0];
85843                                 var sValue = sTag[1];
85844                                 var name = s.name();
85845                                 var dist = utilEditDistance(value, name.substring(0, value.length));
85846                                 var matchesPreset = (pKey === sKey && (!pValue || pValue === sValue));
85847
85848                                 if (dist < 1 || (matchesPreset && dist < 3)) {
85849                                     var obj = {
85850                                         title: name,
85851                                         value: name,
85852                                         suggestion: s,
85853                                         dist: dist + (matchesPreset ? 0 : 1)  // penalize if not matched preset
85854                                     };
85855                                     results.push(obj);
85856                                 }
85857                             }
85858                             results.sort(function(a, b) { return a.dist - b.dist; });
85859                         }
85860                         results = results.slice(0, 10);
85861                         callback(results);
85862                     };
85863                 }
85864
85865
85866                 function addNew() {
85867                     event.preventDefault();
85868                     if (field.locked()) { return; }
85869
85870                     var defaultLang = _mainLocalizer.languageCode().toLowerCase();
85871                     var langExists = _multilingual.find(function(datum) { return datum.lang === defaultLang; });
85872                     var isLangEn = defaultLang.indexOf('en') > -1;
85873                     if (isLangEn || langExists) {
85874                         defaultLang = '';
85875                         langExists = _multilingual.find(function(datum) { return datum.lang === defaultLang; });
85876                     }
85877
85878                     if (!langExists) {
85879                         // prepend the value so it appears at the top
85880                         _multilingual.unshift({ lang: defaultLang, value: '' });
85881
85882                         localizedInputs
85883                             .call(renderMultilingual);
85884                     }
85885                 }
85886
85887
85888                 function change(onInput) {
85889                     return function() {
85890                         if (field.locked()) {
85891                             event.preventDefault();
85892                             return;
85893                         }
85894
85895                         var val = utilGetSetValue(select(this));
85896                         if (!onInput) { val = context.cleanTagValue(val); }
85897
85898                         // don't override multiple values with blank string
85899                         if (!val && Array.isArray(_tags[field.key])) { return; }
85900
85901                         var t = {};
85902
85903                         t[field.key] = val || undefined;
85904                         dispatch$1.call('change', this, t, onInput);
85905                     };
85906                 }
85907             }
85908
85909
85910             function key(lang) {
85911                 return field.key + ':' + lang;
85912             }
85913
85914
85915             function changeLang(d) {
85916                 var tags = {};
85917
85918                 // make sure unrecognized suffixes are lowercase - #7156
85919                 var lang = utilGetSetValue(select(this)).toLowerCase();
85920
85921                 var language = _languagesArray.find(function(d) {
85922                     return d.label.toLowerCase() === lang ||
85923                         (d.localName && d.localName.toLowerCase() === lang) ||
85924                         (d.nativeName && d.nativeName.toLowerCase() === lang);
85925                 });
85926                 if (language) { lang = language.code; }
85927
85928                 if (d.lang && d.lang !== lang) {
85929                     tags[key(d.lang)] = undefined;
85930                 }
85931
85932                 var newKey = lang && context.cleanTagKey(key(lang));
85933
85934                 var value = utilGetSetValue(select(this.parentNode).selectAll('.localized-value'));
85935
85936                 if (newKey && value) {
85937                     tags[newKey] = value;
85938                 } else if (newKey && _wikiTitles && _wikiTitles[d.lang]) {
85939                     tags[newKey] = _wikiTitles[d.lang];
85940                 }
85941
85942                 d.lang = lang;
85943                 dispatch$1.call('change', this, tags);
85944             }
85945
85946
85947             function changeValue(d) {
85948                 if (!d.lang) { return; }
85949                 var value = context.cleanTagValue(utilGetSetValue(select(this))) || undefined;
85950
85951                 // don't override multiple values with blank string
85952                 if (!value && Array.isArray(d.value)) { return; }
85953
85954                 var t = {};
85955                 t[key(d.lang)] = value;
85956                 d.value = value;
85957                 dispatch$1.call('change', this, t);
85958             }
85959
85960
85961             function fetchLanguages(value, cb) {
85962                 var v = value.toLowerCase();
85963
85964                 // show the user's language first
85965                 var langCodes = [_mainLocalizer.localeCode(), _mainLocalizer.languageCode()];
85966
85967                 if (_countryCode && _territoryLanguages[_countryCode]) {
85968                     langCodes = langCodes.concat(_territoryLanguages[_countryCode]);
85969                 }
85970
85971                 var langItems = [];
85972                 langCodes.forEach(function(code) {
85973                     var langItem = _languagesArray.find(function(item) {
85974                         return item.code === code;
85975                     });
85976                     if (langItem) { langItems.push(langItem); }
85977                 });
85978                 langItems = utilArrayUniq(langItems.concat(_languagesArray));
85979
85980                 cb(langItems.filter(function(d) {
85981                     return d.label.toLowerCase().indexOf(v) >= 0 ||
85982                         (d.localName && d.localName.toLowerCase().indexOf(v) >= 0) ||
85983                         (d.nativeName && d.nativeName.toLowerCase().indexOf(v) >= 0) ||
85984                         d.code.toLowerCase().indexOf(v) >= 0;
85985                 }).map(function(d) {
85986                     return { value: d.label };
85987                 }));
85988             }
85989
85990
85991             function renderMultilingual(selection) {
85992                 var entries = selection.selectAll('div.entry')
85993                     .data(_multilingual, function(d) { return d.lang; });
85994
85995                 entries.exit()
85996                     .style('top', '0')
85997                     .style('max-height', '240px')
85998                     .transition()
85999                     .duration(200)
86000                     .style('opacity', '0')
86001                     .style('max-height', '0px')
86002                     .remove();
86003
86004                 var entriesEnter = entries.enter()
86005                     .append('div')
86006                     .attr('class', 'entry')
86007                     .each(function(_, index) {
86008                         var wrap = select(this);
86009
86010                         var domId = utilUniqueDomId(index);
86011
86012                         var label = wrap
86013                             .append('label')
86014                             .attr('class', 'field-label')
86015                             .attr('for', domId);
86016
86017                         var text = label
86018                             .append('span')
86019                             .attr('class', 'label-text');
86020
86021                         text
86022                             .append('span')
86023                             .attr('class', 'label-textvalue')
86024                             .text(_t('translate.localized_translation_label'));
86025
86026                         text
86027                             .append('span')
86028                             .attr('class', 'label-textannotation');
86029
86030                         label
86031                             .append('button')
86032                             .attr('class', 'remove-icon-multilingual')
86033                             .on('click', function(d, index) {
86034                                 if (field.locked()) { return; }
86035                                 event.preventDefault();
86036
86037                                 if (!d.lang || !d.value) {
86038                                     _multilingual.splice(index, 1);
86039                                     renderMultilingual(selection);
86040                                 } else {
86041                                     // remove from entity tags
86042                                     var t = {};
86043                                     t[key(d.lang)] = undefined;
86044                                     dispatch$1.call('change', this, t);
86045                                 }
86046
86047                             })
86048                             .call(svgIcon('#iD-operation-delete'));
86049
86050                         wrap
86051                             .append('input')
86052                             .attr('class', 'localized-lang')
86053                             .attr('id', domId)
86054                             .attr('type', 'text')
86055                             .attr('placeholder', _t('translate.localized_translation_language'))
86056                             .on('blur', changeLang)
86057                             .on('change', changeLang)
86058                             .call(langCombo);
86059
86060                         wrap
86061                             .append('input')
86062                             .attr('type', 'text')
86063                             .attr('class', 'localized-value')
86064                             .on('blur', changeValue)
86065                             .on('change', changeValue);
86066                     });
86067
86068                 entriesEnter
86069                     .style('margin-top', '0px')
86070                     .style('max-height', '0px')
86071                     .style('opacity', '0')
86072                     .transition()
86073                     .duration(200)
86074                     .style('margin-top', '10px')
86075                     .style('max-height', '240px')
86076                     .style('opacity', '1')
86077                     .on('end', function() {
86078                         select(this)
86079                             .style('max-height', '')
86080                             .style('overflow', 'visible');
86081                     });
86082
86083                 entries = entries.merge(entriesEnter);
86084
86085                 entries.order();
86086
86087                 entries.classed('present', function(d) {
86088                     return d.lang && d.value;
86089                 });
86090
86091                 utilGetSetValue(entries.select('.localized-lang'), function(d) {
86092                     return _mainLocalizer.languageName(d.lang);
86093                 });
86094
86095                 utilGetSetValue(entries.select('.localized-value'), function(d) {
86096                         return typeof d.value === 'string' ? d.value : '';
86097                     })
86098                     .attr('title', function(d) {
86099                         return Array.isArray(d.value) ? d.value.filter(Boolean).join('\n') : null;
86100                     })
86101                     .attr('placeholder', function(d) {
86102                         return Array.isArray(d.value) ? _t('inspector.multiple_values') : _t('translate.localized_translation_name');
86103                     })
86104                     .classed('mixed', function(d) {
86105                         return Array.isArray(d.value);
86106                     });
86107             }
86108
86109
86110             localized.tags = function(tags) {
86111                 _tags = tags;
86112
86113                 // Fetch translations from wikipedia
86114                 if (typeof tags.wikipedia === 'string' && !_wikiTitles) {
86115                     _wikiTitles = {};
86116                     var wm = tags.wikipedia.match(/([^:]+):(.+)/);
86117                     if (wm && wm[0] && wm[1]) {
86118                         wikipedia.translations(wm[1], wm[2], function(err, d) {
86119                             if (err || !d) { return; }
86120                             _wikiTitles = d;
86121                         });
86122                     }
86123                 }
86124
86125                 var isMixed = Array.isArray(tags[field.key]);
86126
86127                 utilGetSetValue(input, typeof tags[field.key] === 'string' ? tags[field.key] : '')
86128                     .attr('title', isMixed ? tags[field.key].filter(Boolean).join('\n') : undefined)
86129                     .attr('placeholder', isMixed ? _t('inspector.multiple_values') : field.placeholder())
86130                     .classed('mixed', isMixed);
86131
86132                 calcMultilingual(tags);
86133
86134                 _selection
86135                     .call(localized);
86136             };
86137
86138
86139             localized.focus = function() {
86140                 input.node().focus();
86141             };
86142
86143
86144             localized.entityIDs = function(val) {
86145                 if (!arguments.length) { return _entityIDs; }
86146                 _entityIDs = val;
86147                 _multilingual = [];
86148                 loadCountryCode();
86149                 return localized;
86150             };
86151
86152             function loadCountryCode() {
86153                 var extent = combinedEntityExtent();
86154                 var countryCode = extent && iso1A2Code(extent.center());
86155                 _countryCode = countryCode && countryCode.toLowerCase();
86156             }
86157
86158             function combinedEntityExtent() {
86159                 return _entityIDs && _entityIDs.length && utilTotalExtent(_entityIDs, context.graph());
86160             }
86161
86162             return utilRebind(localized, dispatch$1, 'on');
86163         }
86164
86165         function uiFieldMaxspeed(field, context) {
86166             var dispatch$1 = dispatch('change');
86167             var unitInput = select(null);
86168             var input = select(null);
86169             var _entityIDs = [];
86170             var _tags;
86171             var _isImperial;
86172
86173             var speedCombo = uiCombobox(context, 'maxspeed');
86174             var unitCombo = uiCombobox(context, 'maxspeed-unit')
86175                     .data(['km/h', 'mph'].map(comboValues));
86176
86177             var metricValues = [20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120];
86178             var imperialValues = [5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80];
86179
86180
86181             function maxspeed(selection) {
86182
86183                 var wrap = selection.selectAll('.form-field-input-wrap')
86184                     .data([0]);
86185
86186                 wrap = wrap.enter()
86187                     .append('div')
86188                     .attr('class', 'form-field-input-wrap form-field-input-' + field.type)
86189                     .merge(wrap);
86190
86191
86192                 input = wrap.selectAll('input.maxspeed-number')
86193                     .data([0]);
86194
86195                 input = input.enter()
86196                     .append('input')
86197                     .attr('type', 'text')
86198                     .attr('class', 'maxspeed-number')
86199                     .attr('id', field.domId)
86200                     .call(utilNoAuto)
86201                     .call(speedCombo)
86202                     .merge(input);
86203
86204                 input
86205                     .on('change', change)
86206                     .on('blur', change);
86207
86208                 var loc = combinedEntityExtent().center();
86209                 _isImperial = roadSpeedUnit(loc) === 'mph';
86210
86211                 unitInput = wrap.selectAll('input.maxspeed-unit')
86212                     .data([0]);
86213
86214                 unitInput = unitInput.enter()
86215                     .append('input')
86216                     .attr('type', 'text')
86217                     .attr('class', 'maxspeed-unit')
86218                     .call(unitCombo)
86219                     .merge(unitInput);
86220
86221                 unitInput
86222                     .on('blur', changeUnits)
86223                     .on('change', changeUnits);
86224
86225
86226                 function changeUnits() {
86227                     _isImperial = utilGetSetValue(unitInput) === 'mph';
86228                     utilGetSetValue(unitInput, _isImperial ? 'mph' : 'km/h');
86229                     setUnitSuggestions();
86230                     change();
86231                 }
86232             }
86233
86234
86235             function setUnitSuggestions() {
86236                 speedCombo.data((_isImperial ? imperialValues : metricValues).map(comboValues));
86237                 utilGetSetValue(unitInput, _isImperial ? 'mph' : 'km/h');
86238             }
86239
86240
86241             function comboValues(d) {
86242                 return {
86243                     value: d.toString(),
86244                     title: d.toString()
86245                 };
86246             }
86247
86248
86249             function change() {
86250                 var tag = {};
86251                 var value = utilGetSetValue(input).trim();
86252
86253                 // don't override multiple values with blank string
86254                 if (!value && Array.isArray(_tags[field.key])) { return; }
86255
86256                 if (!value) {
86257                     tag[field.key] = undefined;
86258                 } else if (isNaN(value) || !_isImperial) {
86259                     tag[field.key] = context.cleanTagValue(value);
86260                 } else {
86261                     tag[field.key] = context.cleanTagValue(value + ' mph');
86262                 }
86263
86264                 dispatch$1.call('change', this, tag);
86265             }
86266
86267
86268             maxspeed.tags = function(tags) {
86269                 _tags = tags;
86270
86271                 var value = tags[field.key];
86272                 var isMixed = Array.isArray(value);
86273
86274                 if (!isMixed) {
86275                     if (value && value.indexOf('mph') >= 0) {
86276                         value = parseInt(value, 10).toString();
86277                         _isImperial = true;
86278                     } else if (value) {
86279                         _isImperial = false;
86280                     }
86281                 }
86282
86283                 setUnitSuggestions();
86284
86285                 utilGetSetValue(input, typeof value === 'string' ? value : '')
86286                     .attr('title', isMixed ? value.filter(Boolean).join('\n') : null)
86287                     .attr('placeholder', isMixed ? _t('inspector.multiple_values') : field.placeholder())
86288                     .classed('mixed', isMixed);
86289             };
86290
86291
86292             maxspeed.focus = function() {
86293                 input.node().focus();
86294             };
86295
86296
86297             maxspeed.entityIDs = function(val) {
86298                 _entityIDs = val;
86299             };
86300
86301
86302             function combinedEntityExtent() {
86303                 return _entityIDs && _entityIDs.length && utilTotalExtent(_entityIDs, context.graph());
86304             }
86305
86306
86307             return utilRebind(maxspeed, dispatch$1, 'on');
86308         }
86309
86310         function uiFieldRadio(field, context) {
86311             var dispatch$1 = dispatch('change');
86312             var placeholder = select(null);
86313             var wrap = select(null);
86314             var labels = select(null);
86315             var radios = select(null);
86316             var radioData = (field.options || (field.strings && field.strings.options && Object.keys(field.strings.options)) || field.keys).slice();  // shallow copy
86317             var typeField;
86318             var layerField;
86319             var _oldType = {};
86320             var _entityIDs = [];
86321
86322
86323             function selectedKey() {
86324                 var node = wrap.selectAll('.form-field-input-radio label.active input');
86325                 return !node.empty() && node.datum();
86326             }
86327
86328
86329             function radio(selection) {
86330                 selection.classed('preset-radio', true);
86331
86332                 wrap = selection.selectAll('.form-field-input-wrap')
86333                     .data([0]);
86334
86335                 var enter = wrap.enter()
86336                     .append('div')
86337                     .attr('class', 'form-field-input-wrap form-field-input-radio');
86338
86339                 enter
86340                     .append('span')
86341                     .attr('class', 'placeholder');
86342
86343                 wrap = wrap
86344                     .merge(enter);
86345
86346
86347                 placeholder = wrap.selectAll('.placeholder');
86348
86349                 labels = wrap.selectAll('label')
86350                     .data(radioData);
86351
86352                 enter = labels.enter()
86353                     .append('label');
86354
86355                 enter
86356                     .append('input')
86357                     .attr('type', 'radio')
86358                     .attr('name', field.id)
86359                     .attr('value', function(d) { return field.t('options.' + d, { 'default': d }); })
86360                     .attr('checked', false);
86361
86362                 enter
86363                     .append('span')
86364                     .text(function(d) { return field.t('options.' + d, { 'default': d }); });
86365
86366                 labels = labels
86367                     .merge(enter);
86368
86369                 radios = labels.selectAll('input')
86370                     .on('change', changeRadio);
86371
86372             }
86373
86374
86375             function structureExtras(selection, tags) {
86376                 var selected = selectedKey() || tags.layer !== undefined;
86377                 var type = _mainPresetIndex.field(selected);
86378                 var layer = _mainPresetIndex.field('layer');
86379                 var showLayer = (selected === 'bridge' || selected === 'tunnel' || tags.layer !== undefined);
86380
86381
86382                 var extrasWrap = selection.selectAll('.structure-extras-wrap')
86383                     .data(selected ? [0] : []);
86384
86385                 extrasWrap.exit()
86386                     .remove();
86387
86388                 extrasWrap = extrasWrap.enter()
86389                     .append('div')
86390                     .attr('class', 'structure-extras-wrap')
86391                     .merge(extrasWrap);
86392
86393                 var list = extrasWrap.selectAll('ul')
86394                     .data([0]);
86395
86396                 list = list.enter()
86397                     .append('ul')
86398                     .attr('class', 'rows')
86399                     .merge(list);
86400
86401
86402                 // Type
86403                 if (type) {
86404                     if (!typeField || typeField.id !== selected) {
86405                         typeField = uiField(context, type, _entityIDs, { wrap: false })
86406                             .on('change', changeType);
86407                     }
86408                     typeField.tags(tags);
86409                 } else {
86410                     typeField = null;
86411                 }
86412
86413                 var typeItem = list.selectAll('.structure-type-item')
86414                     .data(typeField ? [typeField] : [], function(d) { return d.id; });
86415
86416                 // Exit
86417                 typeItem.exit()
86418                     .remove();
86419
86420                 // Enter
86421                 var typeEnter = typeItem.enter()
86422                     .insert('li', ':first-child')
86423                     .attr('class', 'labeled-input structure-type-item');
86424
86425                 typeEnter
86426                     .append('span')
86427                     .attr('class', 'label structure-label-type')
86428                     .attr('for', 'preset-input-' + selected)
86429                     .text(_t('inspector.radio.structure.type'));
86430
86431                 typeEnter
86432                     .append('div')
86433                     .attr('class', 'structure-input-type-wrap');
86434
86435                 // Update
86436                 typeItem = typeItem
86437                     .merge(typeEnter);
86438
86439                 if (typeField) {
86440                     typeItem.selectAll('.structure-input-type-wrap')
86441                         .call(typeField.render);
86442                 }
86443
86444
86445                 // Layer
86446                 if (layer && showLayer) {
86447                     if (!layerField) {
86448                         layerField = uiField(context, layer, _entityIDs, { wrap: false })
86449                             .on('change', changeLayer);
86450                     }
86451                     layerField.tags(tags);
86452                     field.keys = utilArrayUnion(field.keys, ['layer']);
86453                 } else {
86454                     layerField = null;
86455                     field.keys = field.keys.filter(function(k) { return k !== 'layer'; });
86456                 }
86457
86458                 var layerItem = list.selectAll('.structure-layer-item')
86459                     .data(layerField ? [layerField] : []);
86460
86461                 // Exit
86462                 layerItem.exit()
86463                     .remove();
86464
86465                 // Enter
86466                 var layerEnter = layerItem.enter()
86467                     .append('li')
86468                     .attr('class', 'labeled-input structure-layer-item');
86469
86470                 layerEnter
86471                     .append('span')
86472                     .attr('class', 'label structure-label-layer')
86473                     .attr('for', 'preset-input-layer')
86474                     .text(_t('inspector.radio.structure.layer'));
86475
86476                 layerEnter
86477                     .append('div')
86478                     .attr('class', 'structure-input-layer-wrap');
86479
86480                 // Update
86481                 layerItem = layerItem
86482                     .merge(layerEnter);
86483
86484                 if (layerField) {
86485                     layerItem.selectAll('.structure-input-layer-wrap')
86486                         .call(layerField.render);
86487                 }
86488             }
86489
86490
86491             function changeType(t, onInput) {
86492                 var key = selectedKey();
86493                 if (!key) { return; }
86494
86495                 var val = t[key];
86496                 if (val !== 'no') {
86497                     _oldType[key] = val;
86498                 }
86499
86500                 if (field.type === 'structureRadio') {
86501                     // remove layer if it should not be set
86502                     if (val === 'no' ||
86503                         (key !== 'bridge' && key !== 'tunnel') ||
86504                         (key === 'tunnel' && val === 'building_passage')) {
86505                         t.layer = undefined;
86506                     }
86507                     // add layer if it should be set
86508                     if (t.layer === undefined) {
86509                         if (key === 'bridge' && val !== 'no') {
86510                             t.layer = '1';
86511                         }
86512                         if (key === 'tunnel' && val !== 'no' && val !== 'building_passage') {
86513                             t.layer = '-1';
86514                         }
86515                     }
86516                  }
86517
86518                 dispatch$1.call('change', this, t, onInput);
86519             }
86520
86521
86522             function changeLayer(t, onInput) {
86523                 if (t.layer === '0') {
86524                     t.layer = undefined;
86525                 }
86526                 dispatch$1.call('change', this, t, onInput);
86527             }
86528
86529
86530             function changeRadio() {
86531                 var t = {};
86532                 var activeKey;
86533
86534                 if (field.key) {
86535                     t[field.key] = undefined;
86536                 }
86537
86538                 radios.each(function(d) {
86539                     var active = select(this).property('checked');
86540                     if (active) { activeKey = d; }
86541
86542                     if (field.key) {
86543                         if (active) { t[field.key] = d; }
86544                     } else {
86545                         var val = _oldType[activeKey] || 'yes';
86546                         t[d] = active ? val : undefined;
86547                     }
86548                 });
86549
86550                 if (field.type === 'structureRadio') {
86551                     if (activeKey === 'bridge') {
86552                         t.layer = '1';
86553                     } else if (activeKey === 'tunnel' && t.tunnel !== 'building_passage') {
86554                         t.layer = '-1';
86555                     } else {
86556                         t.layer = undefined;
86557                     }
86558                 }
86559
86560                 dispatch$1.call('change', this, t);
86561             }
86562
86563
86564             radio.tags = function(tags) {
86565
86566                 radios.property('checked', function(d) {
86567                     if (field.key) {
86568                         return tags[field.key] === d;
86569                     }
86570                     return !!(typeof tags[d] === 'string' && tags[d].toLowerCase() !== 'no');
86571                 });
86572
86573                 function isMixed(d) {
86574                     if (field.key) {
86575                         return Array.isArray(tags[field.key]) && tags[field.key].includes(d);
86576                     }
86577                     return Array.isArray(tags[d]);
86578                 }
86579
86580                 labels
86581                     .classed('active', function(d) {
86582                         if (field.key) {
86583                             return (Array.isArray(tags[field.key]) && tags[field.key].includes(d))
86584                                 || tags[field.key] === d;
86585                         }
86586                         return Array.isArray(tags[d]) || !!(tags[d] && tags[d].toLowerCase() !== 'no');
86587                     })
86588                     .classed('mixed', isMixed)
86589                     .attr('title', function(d) {
86590                         return isMixed(d) ? _t('inspector.unshared_value_tooltip') : null;
86591                     });
86592
86593
86594                 var selection = radios.filter(function() { return this.checked; });
86595
86596                 if (selection.empty()) {
86597                     placeholder.text(_t('inspector.none'));
86598                 } else {
86599                     placeholder.text(selection.attr('value'));
86600                     _oldType[selection.datum()] = tags[selection.datum()];
86601                 }
86602
86603                 if (field.type === 'structureRadio') {
86604                     // For waterways without a tunnel tag, set 'culvert' as
86605                     // the _oldType to default to if the user picks 'tunnel'
86606                     if (!!tags.waterway && !_oldType.tunnel) {
86607                         _oldType.tunnel = 'culvert';
86608                     }
86609
86610                     wrap.call(structureExtras, tags);
86611                 }
86612             };
86613
86614
86615             radio.focus = function() {
86616                 radios.node().focus();
86617             };
86618
86619
86620             radio.entityIDs = function(val) {
86621                 if (!arguments.length) { return _entityIDs; }
86622                 _entityIDs = val;
86623                 _oldType = {};
86624                 return radio;
86625             };
86626
86627
86628             radio.isAllowed = function() {
86629                 return _entityIDs.length === 1;
86630             };
86631
86632
86633             return utilRebind(radio, dispatch$1, 'on');
86634         }
86635
86636         function uiFieldRestrictions(field, context) {
86637             var dispatch$1 = dispatch('change');
86638             var breathe = behaviorBreathe();
86639
86640             corePreferences('turn-restriction-via-way', null);                 // remove old key
86641             var storedViaWay = corePreferences('turn-restriction-via-way0');   // use new key #6922
86642             var storedDistance = corePreferences('turn-restriction-distance');
86643
86644             var _maxViaWay = storedViaWay !== null ? (+storedViaWay) : 0;
86645             var _maxDistance = storedDistance ? (+storedDistance) : 30;
86646             var _initialized = false;
86647             var _parent = select(null);       // the entire field
86648             var _container = select(null);    // just the map
86649             var _oldTurns;
86650             var _graph;
86651             var _vertexID;
86652             var _intersection;
86653             var _fromWayID;
86654
86655             var _lastXPos;
86656
86657
86658             function restrictions(selection) {
86659                 _parent = selection;
86660
86661                 // try to reuse the intersection, but always rebuild it if the graph has changed
86662                 if (_vertexID && (context.graph() !== _graph || !_intersection)) {
86663                     _graph = context.graph();
86664                     _intersection = osmIntersection(_graph, _vertexID, _maxDistance);
86665                 }
86666
86667                 // It's possible for there to be no actual intersection here.
86668                 // for example, a vertex of two `highway=path`
86669                 // In this case, hide the field.
86670                 var isOK = (
86671                     _intersection &&
86672                     _intersection.vertices.length &&           // has vertices
86673                     _intersection.vertices                     // has the vertex that the user selected
86674                         .filter(function(vertex) { return vertex.id === _vertexID; }).length &&
86675                     _intersection.ways.length > 2 &&           // has more than 2 ways
86676                     _intersection.ways                         // has more than 1 TO way
86677                         .filter(function(way) { return way.__to; }).length > 1
86678                 );
86679
86680                 // Also hide in the case where
86681                 select(selection.node().parentNode).classed('hide', !isOK);
86682
86683                 // if form field is hidden or has detached from dom, clean up.
86684                 if (!isOK ||
86685                     !context.container().select('.inspector-wrap.inspector-hidden').empty() ||
86686                     !selection.node().parentNode ||
86687                     !selection.node().parentNode.parentNode) {
86688                     selection.call(restrictions.off);
86689                     return;
86690                 }
86691
86692
86693                 var wrap = selection.selectAll('.form-field-input-wrap')
86694                     .data([0]);
86695
86696                 wrap = wrap.enter()
86697                     .append('div')
86698                     .attr('class', 'form-field-input-wrap form-field-input-' + field.type)
86699                     .merge(wrap);
86700
86701                 var container = wrap.selectAll('.restriction-container')
86702                     .data([0]);
86703
86704                 // enter
86705                 var containerEnter = container.enter()
86706                     .append('div')
86707                     .attr('class', 'restriction-container');
86708
86709                 containerEnter
86710                     .append('div')
86711                     .attr('class', 'restriction-help');
86712
86713                 // update
86714                 _container = containerEnter
86715                     .merge(container)
86716                     .call(renderViewer);
86717
86718                 var controls = wrap.selectAll('.restriction-controls')
86719                     .data([0]);
86720
86721                 // enter/update
86722                 controls.enter()
86723                     .append('div')
86724                     .attr('class', 'restriction-controls-container')
86725                     .append('div')
86726                     .attr('class', 'restriction-controls')
86727                     .merge(controls)
86728                     .call(renderControls);
86729             }
86730
86731
86732             function renderControls(selection) {
86733                 var distControl = selection.selectAll('.restriction-distance')
86734                     .data([0]);
86735
86736                 distControl.exit()
86737                     .remove();
86738
86739                 var distControlEnter = distControl.enter()
86740                     .append('div')
86741                     .attr('class', 'restriction-control restriction-distance');
86742
86743                 distControlEnter
86744                     .append('span')
86745                     .attr('class', 'restriction-control-label restriction-distance-label')
86746                     .text(_t('restriction.controls.distance') + ':');
86747
86748                 distControlEnter
86749                     .append('input')
86750                     .attr('class', 'restriction-distance-input')
86751                     .attr('type', 'range')
86752                     .attr('min', '20')
86753                     .attr('max', '50')
86754                     .attr('step', '5');
86755
86756                 distControlEnter
86757                     .append('span')
86758                     .attr('class', 'restriction-distance-text');
86759
86760                 // update
86761                 selection.selectAll('.restriction-distance-input')
86762                     .property('value', _maxDistance)
86763                     .on('input', function() {
86764                         var val = select(this).property('value');
86765                         _maxDistance = +val;
86766                         _intersection = null;
86767                         _container.selectAll('.layer-osm .layer-turns *').remove();
86768                         corePreferences('turn-restriction-distance', _maxDistance);
86769                         _parent.call(restrictions);
86770                     });
86771
86772                 selection.selectAll('.restriction-distance-text')
86773                     .text(displayMaxDistance(_maxDistance));
86774
86775
86776                 var viaControl = selection.selectAll('.restriction-via-way')
86777                     .data([0]);
86778
86779                 viaControl.exit()
86780                     .remove();
86781
86782                 var viaControlEnter = viaControl.enter()
86783                     .append('div')
86784                     .attr('class', 'restriction-control restriction-via-way');
86785
86786                 viaControlEnter
86787                     .append('span')
86788                     .attr('class', 'restriction-control-label restriction-via-way-label')
86789                     .text(_t('restriction.controls.via') + ':');
86790
86791                 viaControlEnter
86792                     .append('input')
86793                     .attr('class', 'restriction-via-way-input')
86794                     .attr('type', 'range')
86795                     .attr('min', '0')
86796                     .attr('max', '2')
86797                     .attr('step', '1');
86798
86799                 viaControlEnter
86800                     .append('span')
86801                     .attr('class', 'restriction-via-way-text');
86802
86803                 // update
86804                 selection.selectAll('.restriction-via-way-input')
86805                     .property('value', _maxViaWay)
86806                     .on('input', function() {
86807                         var val = select(this).property('value');
86808                         _maxViaWay = +val;
86809                         _container.selectAll('.layer-osm .layer-turns *').remove();
86810                         corePreferences('turn-restriction-via-way0', _maxViaWay);
86811                         _parent.call(restrictions);
86812                     });
86813
86814                 selection.selectAll('.restriction-via-way-text')
86815                     .text(displayMaxVia(_maxViaWay));
86816             }
86817
86818
86819             function renderViewer(selection) {
86820                 if (!_intersection) { return; }
86821
86822                 var vgraph = _intersection.graph;
86823                 var filter = utilFunctor(true);
86824                 var projection = geoRawMercator();
86825
86826                 // Reflow warning: `utilGetDimensions` calls `getBoundingClientRect`
86827                 // Instead of asking the restriction-container for its dimensions,
86828                 //  we can ask the .sidebar, which can have its dimensions cached.
86829                 // width: calc as sidebar - padding
86830                 // height: hardcoded (from `80_app.css`)
86831                 // var d = utilGetDimensions(selection);
86832                 var sdims = utilGetDimensions(context.container().select('.sidebar'));
86833                 var d = [ sdims[0] - 50, 370 ];
86834                 var c = geoVecScale(d, 0.5);
86835                 var z = 22;
86836
86837                 projection.scale(geoZoomToScale(z));
86838
86839                 // Calculate extent of all key vertices
86840                 var extent = geoExtent();
86841                 for (var i = 0; i < _intersection.vertices.length; i++) {
86842                     extent._extend(_intersection.vertices[i].extent());
86843                 }
86844
86845                 // If this is a large intersection, adjust zoom to fit extent
86846                 if (_intersection.vertices.length > 1) {
86847                     var padding = 180;   // in z22 pixels
86848                     var tl = projection([extent[0][0], extent[1][1]]);
86849                     var br = projection([extent[1][0], extent[0][1]]);
86850                     var hFactor = (br[0] - tl[0]) / (d[0] - padding);
86851                     var vFactor = (br[1] - tl[1]) / (d[1] - padding);
86852                     var hZoomDiff = Math.log(Math.abs(hFactor)) / Math.LN2;
86853                     var vZoomDiff = Math.log(Math.abs(vFactor)) / Math.LN2;
86854                     z = z - Math.max(hZoomDiff, vZoomDiff);
86855                     projection.scale(geoZoomToScale(z));
86856                 }
86857
86858                 var padTop = 35;   // reserve top space for hint text
86859                 var extentCenter = projection(extent.center());
86860                 extentCenter[1] = extentCenter[1] - padTop;
86861
86862                 projection
86863                     .translate(geoVecSubtract(c, extentCenter))
86864                     .clipExtent([[0, 0], d]);
86865
86866                 var drawLayers = svgLayers(projection, context).only(['osm','touch']).dimensions(d);
86867                 var drawVertices = svgVertices(projection, context);
86868                 var drawLines = svgLines(projection, context);
86869                 var drawTurns = svgTurns(projection, context);
86870
86871                 var firstTime = selection.selectAll('.surface').empty();
86872
86873                 selection
86874                     .call(drawLayers);
86875
86876                 var surface = selection.selectAll('.surface')
86877                     .classed('tr', true);
86878
86879                 if (firstTime) {
86880                     _initialized = true;
86881
86882                     surface
86883                         .call(breathe);
86884                 }
86885
86886                 // This can happen if we've lowered the detail while a FROM way
86887                 // is selected, and that way is no longer part of the intersection.
86888                 if (_fromWayID && !vgraph.hasEntity(_fromWayID)) {
86889                     _fromWayID = null;
86890                     _oldTurns = null;
86891                 }
86892
86893                 surface
86894                     .call(utilSetDimensions, d)
86895                     .call(drawVertices, vgraph, _intersection.vertices, filter, extent, z)
86896                     .call(drawLines, vgraph, _intersection.ways, filter)
86897                     .call(drawTurns, vgraph, _intersection.turns(_fromWayID, _maxViaWay));
86898
86899                 surface
86900                     .on('click.restrictions', click)
86901                     .on('mouseover.restrictions', mouseover);
86902
86903                 surface
86904                     .selectAll('.selected')
86905                     .classed('selected', false);
86906
86907                 surface
86908                     .selectAll('.related')
86909                     .classed('related', false);
86910
86911                 if (_fromWayID) {
86912                     var way = vgraph.entity(_fromWayID);
86913                     surface
86914                         .selectAll('.' + _fromWayID)
86915                         .classed('selected', true)
86916                         .classed('related', true);
86917                 }
86918
86919                 document.addEventListener('resizeWindow', function () {
86920                     utilSetDimensions(_container, null);
86921                     redraw(1);
86922                 }, false);
86923
86924                 updateHints(null);
86925
86926
86927                 function click() {
86928                     surface
86929                         .call(breathe.off)
86930                         .call(breathe);
86931
86932                     var datum = event.target.__data__;
86933                     var entity = datum && datum.properties && datum.properties.entity;
86934                     if (entity) {
86935                         datum = entity;
86936                     }
86937
86938                     if (datum instanceof osmWay && (datum.__from || datum.__via)) {
86939                         _fromWayID = datum.id;
86940                         _oldTurns = null;
86941                         redraw();
86942
86943                     } else if (datum instanceof osmTurn) {
86944                         var actions, extraActions, turns, i;
86945                         var restrictionType = osmInferRestriction(vgraph, datum, projection);
86946
86947                         if (datum.restrictionID && !datum.direct) {
86948                             return;
86949
86950                         } else if (datum.restrictionID && !datum.only) {    // NO -> ONLY
86951                             var seen = {};
86952                             var datumOnly = JSON.parse(JSON.stringify(datum));   // deep clone the datum
86953                             datumOnly.only = true;                               // but change this property
86954                             restrictionType = restrictionType.replace(/^no/, 'only');
86955
86956                             // Adding an ONLY restriction should destroy all other direct restrictions from the FROM towards the VIA.
86957                             // We will remember them in _oldTurns, and restore them if the user clicks again.
86958                             turns = _intersection.turns(_fromWayID, 2);
86959                             extraActions = [];
86960                             _oldTurns = [];
86961                             for (i = 0; i < turns.length; i++) {
86962                                 var turn = turns[i];
86963                                 if (seen[turn.restrictionID]) { continue; }  // avoid deleting the turn twice (#4968, #4928)
86964
86965                                 if (turn.direct && turn.path[1] === datum.path[1]) {
86966                                     seen[turns[i].restrictionID] = true;
86967                                     turn.restrictionType = osmInferRestriction(vgraph, turn, projection);
86968                                     _oldTurns.push(turn);
86969                                     extraActions.push(actionUnrestrictTurn(turn));
86970                                 }
86971                             }
86972
86973                             actions = _intersection.actions.concat(extraActions, [
86974                                 actionRestrictTurn(datumOnly, restrictionType),
86975                                 _t('operations.restriction.annotation.create')
86976                             ]);
86977
86978                         } else if (datum.restrictionID) {   // ONLY -> Allowed
86979                             // Restore whatever restrictions we might have destroyed by cycling thru the ONLY state.
86980                             // This relies on the assumption that the intersection was already split up when we
86981                             // performed the previous action (NO -> ONLY), so the IDs in _oldTurns shouldn't have changed.
86982                             turns = _oldTurns || [];
86983                             extraActions = [];
86984                             for (i = 0; i < turns.length; i++) {
86985                                 if (turns[i].key !== datum.key) {
86986                                     extraActions.push(actionRestrictTurn(turns[i], turns[i].restrictionType));
86987                                 }
86988                             }
86989                             _oldTurns = null;
86990
86991                             actions = _intersection.actions.concat(extraActions, [
86992                                 actionUnrestrictTurn(datum),
86993                                 _t('operations.restriction.annotation.delete')
86994                             ]);
86995
86996                         } else {    // Allowed -> NO
86997                             actions = _intersection.actions.concat([
86998                                 actionRestrictTurn(datum, restrictionType),
86999                                 _t('operations.restriction.annotation.create')
87000                             ]);
87001                         }
87002
87003                         context.perform.apply(context, actions);
87004
87005                         // At this point the datum will be changed, but will have same key..
87006                         // Refresh it and update the help..
87007                         var s = surface.selectAll('.' + datum.key);
87008                         datum = s.empty() ? null : s.datum();
87009                         updateHints(datum);
87010
87011                     } else {
87012                         _fromWayID = null;
87013                         _oldTurns = null;
87014                         redraw();
87015                     }
87016                 }
87017
87018
87019                 function mouseover() {
87020                     var datum = event.target.__data__;
87021                     updateHints(datum);
87022                 }
87023
87024                 _lastXPos = _lastXPos || sdims[0];
87025
87026                 function redraw(minChange) {
87027                     var xPos = -1;
87028
87029                     if (minChange) {
87030                         xPos = utilGetDimensions(context.container().select('.sidebar'))[0];
87031                     }
87032
87033                     if (!minChange || (minChange && Math.abs(xPos - _lastXPos) >= minChange)) {
87034                         if (context.hasEntity(_vertexID)) {
87035                             _lastXPos = xPos;
87036                             _container.call(renderViewer);
87037                         }
87038                     }
87039                 }
87040
87041
87042                 function highlightPathsFrom(wayID) {
87043                     surface.selectAll('.related')
87044                         .classed('related', false)
87045                         .classed('allow', false)
87046                         .classed('restrict', false)
87047                         .classed('only', false);
87048
87049                     surface.selectAll('.' + wayID)
87050                         .classed('related', true);
87051
87052                     if (wayID) {
87053                         var turns = _intersection.turns(wayID, _maxViaWay);
87054                         for (var i = 0; i < turns.length; i++) {
87055                             var turn = turns[i];
87056                             var ids = [turn.to.way];
87057                             var klass = (turn.no ? 'restrict' : (turn.only ? 'only' : 'allow'));
87058
87059                             if (turn.only || turns.length === 1) {
87060                                 if (turn.via.ways) {
87061                                     ids = ids.concat(turn.via.ways);
87062                                 }
87063                             } else if (turn.to.way === wayID) {
87064                                 continue;
87065                             }
87066
87067                             surface.selectAll(utilEntitySelector(ids))
87068                                 .classed('related', true)
87069                                 .classed('allow', (klass === 'allow'))
87070                                 .classed('restrict', (klass === 'restrict'))
87071                                 .classed('only', (klass === 'only'));
87072                         }
87073                     }
87074                 }
87075
87076
87077                 function updateHints(datum) {
87078                     var help = _container.selectAll('.restriction-help').html('');
87079
87080                     var placeholders = {};
87081                     ['from', 'via', 'to'].forEach(function(k) {
87082                         placeholders[k] = '<span class="qualifier">' + _t('restriction.help.' + k) + '</span>';
87083                     });
87084
87085                     var entity = datum && datum.properties && datum.properties.entity;
87086                     if (entity) {
87087                         datum = entity;
87088                     }
87089
87090                     if (_fromWayID) {
87091                         way = vgraph.entity(_fromWayID);
87092                         surface
87093                             .selectAll('.' + _fromWayID)
87094                             .classed('selected', true)
87095                             .classed('related', true);
87096                     }
87097
87098                     // Hovering a way
87099                     if (datum instanceof osmWay && datum.__from) {
87100                         way = datum;
87101
87102                         highlightPathsFrom(_fromWayID ? null : way.id);
87103                         surface.selectAll('.' + way.id)
87104                             .classed('related', true);
87105
87106                         var clickSelect = (!_fromWayID || _fromWayID !== way.id);
87107                         help
87108                             .append('div')      // "Click to select FROM {fromName}." / "FROM {fromName}"
87109                             .html(_t('restriction.help.' + (clickSelect ? 'select_from_name' : 'from_name'), {
87110                                 from: placeholders.from,
87111                                 fromName: displayName(way.id, vgraph)
87112                             }));
87113
87114
87115                     // Hovering a turn arrow
87116                     } else if (datum instanceof osmTurn) {
87117                         var restrictionType = osmInferRestriction(vgraph, datum, projection);
87118                         var turnType = restrictionType.replace(/^(only|no)\_/, '');
87119                         var indirect = (datum.direct === false ? _t('restriction.help.indirect') : '');
87120                         var klass, turnText, nextText;
87121
87122                         if (datum.no) {
87123                             klass = 'restrict';
87124                             turnText = _t('restriction.help.turn.no_' + turnType, { indirect: indirect });
87125                             nextText = _t('restriction.help.turn.only_' + turnType, { indirect: '' });
87126                         } else if (datum.only) {
87127                             klass = 'only';
87128                             turnText = _t('restriction.help.turn.only_' + turnType, { indirect: indirect });
87129                             nextText = _t('restriction.help.turn.allowed_' + turnType, { indirect: '' });
87130                         } else {
87131                             klass = 'allow';
87132                             turnText = _t('restriction.help.turn.allowed_' + turnType, { indirect: indirect });
87133                             nextText = _t('restriction.help.turn.no_' + turnType, { indirect: '' });
87134                         }
87135
87136                         help
87137                             .append('div')      // "NO Right Turn (indirect)"
87138                             .attr('class', 'qualifier ' + klass)
87139                             .text(turnText);
87140
87141                         help
87142                             .append('div')      // "FROM {fromName} TO {toName}"
87143                             .html(_t('restriction.help.from_name_to_name', {
87144                                 from: placeholders.from,
87145                                 fromName: displayName(datum.from.way, vgraph),
87146                                 to: placeholders.to,
87147                                 toName: displayName(datum.to.way, vgraph)
87148                             }));
87149
87150                         if (datum.via.ways && datum.via.ways.length) {
87151                             var names = [];
87152                             for (var i = 0; i < datum.via.ways.length; i++) {
87153                                 var prev = names[names.length - 1];
87154                                 var curr = displayName(datum.via.ways[i], vgraph);
87155                                 if (!prev || curr !== prev)   // collapse identical names
87156                                     { names.push(curr); }
87157                             }
87158
87159                             help
87160                                 .append('div')      // "VIA {viaNames}"
87161                                 .html(_t('restriction.help.via_names', {
87162                                     via: placeholders.via,
87163                                     viaNames: names.join(', ')
87164                                 }));
87165                         }
87166
87167                         if (!indirect) {
87168                             help
87169                                 .append('div')      // Click for "No Right Turn"
87170                                 .text(_t('restriction.help.toggle', { turn: nextText.trim() }));
87171                         }
87172
87173                         highlightPathsFrom(null);
87174                         var alongIDs = datum.path.slice();
87175                         surface.selectAll(utilEntitySelector(alongIDs))
87176                             .classed('related', true)
87177                             .classed('allow', (klass === 'allow'))
87178                             .classed('restrict', (klass === 'restrict'))
87179                             .classed('only', (klass === 'only'));
87180
87181
87182                     // Hovering empty surface
87183                     } else {
87184                         highlightPathsFrom(null);
87185                         if (_fromWayID) {
87186                             help
87187                                 .append('div')      // "FROM {fromName}"
87188                                 .html(_t('restriction.help.from_name', {
87189                                     from: placeholders.from,
87190                                     fromName: displayName(_fromWayID, vgraph)
87191                                 }));
87192
87193                         } else {
87194                             help
87195                                 .append('div')      // "Click to select a FROM segment."
87196                                 .html(_t('restriction.help.select_from', {
87197                                     from: placeholders.from
87198                                 }));
87199                         }
87200                     }
87201                 }
87202             }
87203
87204
87205             function displayMaxDistance(maxDist) {
87206                 var isImperial = !_mainLocalizer.usesMetric();
87207                 var opts;
87208
87209                 if (isImperial) {
87210                     var distToFeet = {   // imprecise conversion for prettier display
87211                         20: 70, 25: 85, 30: 100, 35: 115, 40: 130, 45: 145, 50: 160
87212                     }[maxDist];
87213                     opts = { distance: _t('units.feet', { quantity: distToFeet }) };
87214                 } else {
87215                     opts = { distance: _t('units.meters', { quantity: maxDist }) };
87216                 }
87217
87218                 return _t('restriction.controls.distance_up_to', opts);
87219             }
87220
87221
87222             function displayMaxVia(maxVia) {
87223                 return maxVia === 0 ? _t('restriction.controls.via_node_only')
87224                     : maxVia === 1 ? _t('restriction.controls.via_up_to_one')
87225                     : _t('restriction.controls.via_up_to_two');
87226             }
87227
87228
87229             function displayName(entityID, graph) {
87230                 var entity = graph.entity(entityID);
87231                 var name = utilDisplayName(entity) || '';
87232                 var matched = _mainPresetIndex.match(entity, graph);
87233                 var type = (matched && matched.name()) || utilDisplayType(entity.id);
87234                 return name || type;
87235             }
87236
87237
87238             restrictions.entityIDs = function(val) {
87239                 _intersection = null;
87240                 _fromWayID = null;
87241                 _oldTurns = null;
87242                 _vertexID = val[0];
87243             };
87244
87245
87246             restrictions.tags = function() {};
87247             restrictions.focus = function() {};
87248
87249
87250             restrictions.off = function(selection) {
87251                 if (!_initialized) { return; }
87252
87253                 selection.selectAll('.surface')
87254                     .call(breathe.off)
87255                     .on('click.restrictions', null)
87256                     .on('mouseover.restrictions', null);
87257
87258                 select(window)
87259                     .on('resize.restrictions', null);
87260             };
87261
87262
87263             return utilRebind(restrictions, dispatch$1, 'on');
87264         }
87265
87266         uiFieldRestrictions.supportsMultiselection = false;
87267
87268         function uiFieldTextarea(field, context) {
87269             var dispatch$1 = dispatch('change');
87270             var input = select(null);
87271             var _tags;
87272
87273
87274             function textarea(selection) {
87275                 var wrap = selection.selectAll('.form-field-input-wrap')
87276                     .data([0]);
87277
87278                 wrap = wrap.enter()
87279                     .append('div')
87280                     .attr('class', 'form-field-input-wrap form-field-input-' + field.type)
87281                     .merge(wrap);
87282
87283                 input = wrap.selectAll('textarea')
87284                     .data([0]);
87285
87286                 input = input.enter()
87287                     .append('textarea')
87288                     .attr('id', field.domId)
87289                     .call(utilNoAuto)
87290                     .on('input', change(true))
87291                     .on('blur', change())
87292                     .on('change', change())
87293                     .merge(input);
87294             }
87295
87296
87297             function change(onInput) {
87298                 return function() {
87299
87300                     var val = utilGetSetValue(input);
87301                     if (!onInput) { val = context.cleanTagValue(val); }
87302
87303                     // don't override multiple values with blank string
87304                     if (!val && Array.isArray(_tags[field.key])) { return; }
87305
87306                     var t = {};
87307                     t[field.key] = val || undefined;
87308                     dispatch$1.call('change', this, t, onInput);
87309                 };
87310             }
87311
87312
87313             textarea.tags = function(tags) {
87314                 _tags = tags;
87315
87316                 var isMixed = Array.isArray(tags[field.key]);
87317
87318                 utilGetSetValue(input, !isMixed && tags[field.key] ? tags[field.key] : '')
87319                     .attr('title', isMixed ? tags[field.key].filter(Boolean).join('\n') : undefined)
87320                     .attr('placeholder', isMixed ? _t('inspector.multiple_values') : (field.placeholder() || _t('inspector.unknown')))
87321                     .classed('mixed', isMixed);
87322             };
87323
87324
87325             textarea.focus = function() {
87326                 input.node().focus();
87327             };
87328
87329
87330             return utilRebind(textarea, dispatch$1, 'on');
87331         }
87332
87333         function uiFieldWikidata(field, context) {
87334             var wikidata = services.wikidata;
87335             var dispatch$1 = dispatch('change');
87336
87337             var _selection = select(null);
87338             var _searchInput = select(null);
87339             var _qid = null;
87340             var _wikidataEntity = null;
87341             var _wikiURL = '';
87342             var _entityIDs = [];
87343
87344             var _wikipediaKey = field.keys && field.keys.find(function(key) {
87345                     return key.includes('wikipedia');
87346                 }),
87347                 _hintKey = field.key === 'wikidata' ? 'name' : field.key.split(':')[0];
87348
87349             var combobox = uiCombobox(context, 'combo-' + field.safeid)
87350                 .caseSensitive(true)
87351                 .minItems(1);
87352
87353             function wiki(selection) {
87354
87355                 _selection = selection;
87356
87357                 var wrap = selection.selectAll('.form-field-input-wrap')
87358                     .data([0]);
87359
87360                 wrap = wrap.enter()
87361                     .append('div')
87362                     .attr('class', 'form-field-input-wrap form-field-input-' + field.type)
87363                     .merge(wrap);
87364
87365
87366                 var list = wrap.selectAll('ul')
87367                     .data([0]);
87368
87369                 list = list.enter()
87370                     .append('ul')
87371                     .attr('class', 'rows')
87372                     .merge(list);
87373
87374                 var searchRow = list.selectAll('li.wikidata-search')
87375                     .data([0]);
87376
87377                 var searchRowEnter = searchRow.enter()
87378                     .append('li')
87379                     .attr('class', 'wikidata-search');
87380
87381                 searchRowEnter
87382                     .append('input')
87383                     .attr('type', 'text')
87384                     .attr('id', field.domId)
87385                     .style('flex', '1')
87386                     .call(utilNoAuto)
87387                     .on('focus', function() {
87388                         var node = select(this).node();
87389                         node.setSelectionRange(0, node.value.length);
87390                     })
87391                     .on('blur', function() {
87392                         setLabelForEntity();
87393                     })
87394                     .call(combobox.fetcher(fetchWikidataItems));
87395
87396                 combobox.on('accept', function(d) {
87397                     _qid = d.id;
87398                     change();
87399                 }).on('cancel', function() {
87400                     setLabelForEntity();
87401                 });
87402
87403                 searchRowEnter
87404                     .append('button')
87405                     .attr('class', 'form-field-button wiki-link')
87406                     .attr('title', _t('icons.view_on', { domain: 'wikidata.org' }))
87407                     .attr('tabindex', -1)
87408                     .call(svgIcon('#iD-icon-out-link'))
87409                     .on('click', function() {
87410                         event.preventDefault();
87411                         if (_wikiURL) { window.open(_wikiURL, '_blank'); }
87412                     });
87413
87414                 searchRow = searchRow.merge(searchRowEnter);
87415
87416                 _searchInput = searchRow.select('input');
87417
87418                 var wikidataProperties = ['description', 'identifier'];
87419
87420                 var items = list.selectAll('li.labeled-input')
87421                     .data(wikidataProperties);
87422
87423                 // Enter
87424                 var enter = items.enter()
87425                     .append('li')
87426                     .attr('class', function(d) { return 'labeled-input preset-wikidata-' + d; });
87427
87428                 enter
87429                     .append('span')
87430                     .attr('class', 'label')
87431                     .text(function(d) { return _t('wikidata.' + d); });
87432
87433                 enter
87434                     .append('input')
87435                     .attr('type', 'text')
87436                     .call(utilNoAuto)
87437                     .classed('disabled', 'true')
87438                     .attr('readonly', 'true');
87439
87440                 enter
87441                     .append('button')
87442                     .attr('class', 'form-field-button')
87443                     .attr('title', _t('icons.copy'))
87444                     .attr('tabindex', -1)
87445                     .call(svgIcon('#iD-operation-copy'))
87446                     .on('click', function() {
87447                         event.preventDefault();
87448                         select(this.parentNode)
87449                             .select('input')
87450                             .node()
87451                             .select();
87452                         document.execCommand('copy');
87453                     });
87454
87455             }
87456
87457             function fetchWikidataItems(q, callback) {
87458
87459                 if (!q && _hintKey) {
87460                     // other tags may be good search terms
87461                     for (var i in _entityIDs) {
87462                         var entity = context.hasEntity(_entityIDs[i]);
87463                         if (entity.tags[_hintKey]) {
87464                             q = entity.tags[_hintKey];
87465                             break;
87466                         }
87467                     }
87468                 }
87469
87470                 wikidata.itemsForSearchQuery(q, function(err, data) {
87471                     if (err) { return; }
87472
87473                     for (var i in data) {
87474                         data[i].value = data[i].label + ' (' +  data[i].id + ')';
87475                         data[i].title = data[i].description;
87476                     }
87477
87478                     if (callback) { callback(data); }
87479                 });
87480             }
87481
87482
87483             function change() {
87484                 var syncTags = {};
87485                 syncTags[field.key] = _qid;
87486                 dispatch$1.call('change', this, syncTags);
87487
87488                 // attempt asynchronous update of wikidata tag..
87489                 var initGraph = context.graph();
87490                 var initEntityIDs = _entityIDs;
87491
87492                 wikidata.entityByQID(_qid, function(err, entity) {
87493                     if (err) { return; }
87494
87495                     // If graph has changed, we can't apply this update.
87496                     if (context.graph() !== initGraph) { return; }
87497
87498                     if (!entity.sitelinks) { return; }
87499
87500                     var langs = wikidata.languagesToQuery();
87501                     // use the label and description languages as fallbacks
87502                     ['labels', 'descriptions'].forEach(function(key) {
87503                         if (!entity[key]) { return; }
87504
87505                         var valueLangs = Object.keys(entity[key]);
87506                         if (valueLangs.length === 0) { return; }
87507                         var valueLang = valueLangs[0];
87508
87509                         if (langs.indexOf(valueLang) === -1) {
87510                             langs.push(valueLang);
87511                         }
87512                     });
87513
87514                     var newWikipediaValue;
87515
87516                     if (_wikipediaKey) {
87517                         var foundPreferred;
87518                         for (var i in langs) {
87519                             var lang = langs[i];
87520                             var siteID = lang.replace('-', '_') + 'wiki';
87521                             if (entity.sitelinks[siteID]) {
87522                                 foundPreferred = true;
87523                                 newWikipediaValue = lang + ':' + entity.sitelinks[siteID].title;
87524                                 // use the first match
87525                                 break;
87526                             }
87527                         }
87528
87529                         if (!foundPreferred) {
87530                             // No wikipedia sites available in the user's language or the fallback languages,
87531                             // default to any wikipedia sitelink
87532
87533                             var wikiSiteKeys = Object.keys(entity.sitelinks).filter(function(site) {
87534                                 return site.endsWith('wiki');
87535                             });
87536
87537                             if (wikiSiteKeys.length === 0) {
87538                                 // if no wikipedia pages are linked to this wikidata entity, delete that tag
87539                                 newWikipediaValue = null;
87540                             } else {
87541                                 var wikiLang = wikiSiteKeys[0].slice(0, -4).replace('_', '-');
87542                                 var wikiTitle = entity.sitelinks[wikiSiteKeys[0]].title;
87543                                 newWikipediaValue = wikiLang + ':' + wikiTitle;
87544                             }
87545                         }
87546                     }
87547
87548                     if (newWikipediaValue) {
87549                         newWikipediaValue = context.cleanTagValue(newWikipediaValue);
87550                     }
87551
87552                     if (typeof newWikipediaValue === 'undefined') { return; }
87553
87554                     var actions = initEntityIDs.map(function(entityID) {
87555                         var entity = context.hasEntity(entityID);
87556                         if (!entity) { return; }
87557
87558                         var currTags = Object.assign({}, entity.tags);  // shallow copy
87559                         if (newWikipediaValue === null) {
87560                             if (!currTags[_wikipediaKey]) { return; }
87561
87562                             delete currTags[_wikipediaKey];
87563                         } else {
87564                             currTags[_wikipediaKey] = newWikipediaValue;
87565                         }
87566
87567                         return actionChangeTags(entityID, currTags);
87568                     }).filter(Boolean);
87569
87570                     if (!actions.length) { return; }
87571
87572                     // Coalesce the update of wikidata tag into the previous tag change
87573                     context.overwrite(
87574                         function actionUpdateWikipediaTags(graph) {
87575                             actions.forEach(function(action) {
87576                                 graph = action(graph);
87577                             });
87578                             return graph;
87579                         },
87580                         context.history().undoAnnotation()
87581                     );
87582
87583                     // do not dispatch.call('change') here, because entity_editor
87584                     // changeTags() is not intended to be called asynchronously
87585                 });
87586             }
87587
87588             function setLabelForEntity() {
87589                 var label = '';
87590                 if (_wikidataEntity) {
87591                     label = entityPropertyForDisplay(_wikidataEntity, 'labels');
87592                     if (label.length === 0) {
87593                         label = _wikidataEntity.id.toString();
87594                     }
87595                 }
87596                 utilGetSetValue(_searchInput, label);
87597             }
87598
87599
87600             wiki.tags = function(tags) {
87601
87602                 var isMixed = Array.isArray(tags[field.key]);
87603                 _searchInput
87604                     .attr('title', isMixed ? tags[field.key].filter(Boolean).join('\n') : null)
87605                     .attr('placeholder', isMixed ? _t('inspector.multiple_values') : '')
87606                     .classed('mixed', isMixed);
87607
87608                 _qid = typeof tags[field.key] === 'string' && tags[field.key] || '';
87609
87610                 if (!/^Q[0-9]*$/.test(_qid)) {   // not a proper QID
87611                     unrecognized();
87612                     return;
87613                 }
87614
87615                 // QID value in correct format
87616                 _wikiURL = 'https://wikidata.org/wiki/' + _qid;
87617                 wikidata.entityByQID(_qid, function(err, entity) {
87618                     if (err) {
87619                         unrecognized();
87620                         return;
87621                     }
87622                     _wikidataEntity = entity;
87623
87624                     setLabelForEntity();
87625
87626                     var description = entityPropertyForDisplay(entity, 'descriptions');
87627
87628                     _selection.select('button.wiki-link')
87629                         .classed('disabled', false);
87630
87631                     _selection.select('.preset-wikidata-description')
87632                         .style('display', function(){
87633                             return description.length > 0 ? 'flex' : 'none';
87634                         })
87635                         .select('input')
87636                         .attr('value', description);
87637
87638                     _selection.select('.preset-wikidata-identifier')
87639                         .style('display', function(){
87640                             return entity.id ? 'flex' : 'none';
87641                         })
87642                         .select('input')
87643                         .attr('value', entity.id);
87644                 });
87645
87646
87647                 // not a proper QID
87648                 function unrecognized() {
87649                     _wikidataEntity = null;
87650                     setLabelForEntity();
87651
87652                     _selection.select('.preset-wikidata-description')
87653                         .style('display', 'none');
87654                     _selection.select('.preset-wikidata-identifier')
87655                         .style('display', 'none');
87656
87657                     _selection.select('button.wiki-link')
87658                         .classed('disabled', true);
87659
87660                     if (_qid && _qid !== '') {
87661                         _wikiURL = 'https://wikidata.org/wiki/Special:Search?search=' + _qid;
87662                     } else {
87663                         _wikiURL = '';
87664                     }
87665                 }
87666             };
87667
87668             function entityPropertyForDisplay(wikidataEntity, propKey) {
87669                 if (!wikidataEntity[propKey]) { return ''; }
87670                 var propObj = wikidataEntity[propKey];
87671                 var langKeys = Object.keys(propObj);
87672                 if (langKeys.length === 0) { return ''; }
87673                 // sorted by priority, since we want to show the user's language first if possible
87674                 var langs = wikidata.languagesToQuery();
87675                 for (var i in langs) {
87676                     var lang = langs[i];
87677                     var valueObj = propObj[lang];
87678                     if (valueObj && valueObj.value && valueObj.value.length > 0) { return valueObj.value; }
87679                 }
87680                 // default to any available value
87681                 return propObj[langKeys[0]].value;
87682             }
87683
87684
87685             wiki.entityIDs = function(val) {
87686                 if (!arguments.length) { return _entityIDs; }
87687                 _entityIDs = val;
87688                 return wiki;
87689             };
87690
87691
87692             wiki.focus = function() {
87693                 _searchInput.node().focus();
87694             };
87695
87696
87697             return utilRebind(wiki, dispatch$1, 'on');
87698         }
87699
87700         function uiFieldWikipedia(field, context) {
87701           var arguments$1 = arguments;
87702
87703           var dispatch$1 = dispatch('change');
87704           var wikipedia = services.wikipedia;
87705           var wikidata = services.wikidata;
87706           var _langInput = select(null);
87707           var _titleInput = select(null);
87708           var _wikiURL = '';
87709           var _entityIDs;
87710           var _tags;
87711
87712           var _dataWikipedia = [];
87713           _mainFileFetcher.get('wmf_sitematrix')
87714             .then(function (d) {
87715               _dataWikipedia = d;
87716               if (_tags) { updateForTags(_tags); }
87717             })
87718             .catch(function () { /* ignore */ });
87719
87720
87721           var langCombo = uiCombobox(context, 'wikipedia-lang')
87722             .fetcher(function (value, callback) {
87723               var v = value.toLowerCase();
87724               callback(_dataWikipedia
87725                 .filter(function (d) {
87726                   return d[0].toLowerCase().indexOf(v) >= 0 ||
87727                     d[1].toLowerCase().indexOf(v) >= 0 ||
87728                     d[2].toLowerCase().indexOf(v) >= 0;
87729                 })
87730                 .map(function (d) { return ({ value: d[1] }); })
87731               );
87732             });
87733
87734           var titleCombo = uiCombobox(context, 'wikipedia-title')
87735             .fetcher(function (value, callback) {
87736               if (!value) {
87737                 value = '';
87738                 for (var i in _entityIDs) {
87739                   var entity = context.hasEntity(_entityIDs[i]);
87740                   if (entity.tags.name) {
87741                     value = entity.tags.name;
87742                     break;
87743                   }
87744                 }
87745               }
87746               var searchfn = value.length > 7 ? wikipedia.search : wikipedia.suggestions;
87747               searchfn(language()[2], value, function (query, data) {
87748                 callback( data.map(function (d) { return ({ value: d }); }) );
87749               });
87750             });
87751
87752
87753           function wiki(selection) {
87754             var wrap = selection.selectAll('.form-field-input-wrap')
87755               .data([0]);
87756
87757             wrap = wrap.enter()
87758               .append('div')
87759               .attr('class', ("form-field-input-wrap form-field-input-" + (field.type)))
87760               .merge(wrap);
87761
87762
87763             var langContainer = wrap.selectAll('.wiki-lang-container')
87764               .data([0]);
87765
87766             langContainer = langContainer.enter()
87767               .append('div')
87768               .attr('class', 'wiki-lang-container')
87769               .merge(langContainer);
87770
87771
87772             _langInput = langContainer.selectAll('input.wiki-lang')
87773               .data([0]);
87774
87775             _langInput = _langInput.enter()
87776               .append('input')
87777               .attr('type', 'text')
87778               .attr('class', 'wiki-lang')
87779               .attr('placeholder', _t('translate.localized_translation_language'))
87780               .call(utilNoAuto)
87781               .call(langCombo)
87782               .merge(_langInput);
87783
87784             _langInput
87785               .on('blur', changeLang)
87786               .on('change', changeLang);
87787
87788
87789             var titleContainer = wrap.selectAll('.wiki-title-container')
87790               .data([0]);
87791
87792             titleContainer = titleContainer.enter()
87793               .append('div')
87794               .attr('class', 'wiki-title-container')
87795               .merge(titleContainer);
87796
87797             _titleInput = titleContainer.selectAll('input.wiki-title')
87798               .data([0]);
87799
87800             _titleInput = _titleInput.enter()
87801               .append('input')
87802               .attr('type', 'text')
87803               .attr('class', 'wiki-title')
87804               .attr('id', field.domId)
87805               .call(utilNoAuto)
87806               .call(titleCombo)
87807               .merge(_titleInput);
87808
87809             _titleInput
87810               .on('blur', blur)
87811               .on('change', change);
87812
87813
87814             var link = titleContainer.selectAll('.wiki-link')
87815               .data([0]);
87816
87817             link = link.enter()
87818               .append('button')
87819               .attr('class', 'form-field-button wiki-link')
87820               .attr('tabindex', -1)
87821               .attr('title', _t('icons.view_on', { domain: 'wikipedia.org' }))
87822               .call(svgIcon('#iD-icon-out-link'))
87823               .merge(link);
87824
87825             link
87826               .on('click', function () {
87827                 event.preventDefault();
87828                 if (_wikiURL) { window.open(_wikiURL, '_blank'); }
87829               });
87830           }
87831
87832
87833           function defaultLanguageInfo(skipEnglishFallback) {
87834             var langCode = _mainLocalizer.languageCode().toLowerCase();
87835
87836             for (var i in _dataWikipedia) {
87837               var d = _dataWikipedia[i];
87838               // default to the language of iD's current locale
87839               if (d[2] === langCode) { return d; }
87840             }
87841
87842             // fallback to English
87843             return skipEnglishFallback ? ['', '', ''] : ['English', 'English', 'en'];
87844           }
87845
87846
87847           function language(skipEnglishFallback) {
87848             var value = utilGetSetValue(_langInput).toLowerCase();
87849
87850             for (var i in _dataWikipedia) {
87851               var d = _dataWikipedia[i];
87852               // return the language already set in the UI, if supported
87853               if (d[0].toLowerCase() === value ||
87854                 d[1].toLowerCase() === value ||
87855                 d[2] === value) { return d; }
87856             }
87857
87858             // fallback to English
87859             return defaultLanguageInfo(skipEnglishFallback);
87860           }
87861
87862
87863           function changeLang() {
87864             utilGetSetValue(_langInput, language()[1]);
87865             change(true);
87866           }
87867
87868
87869           function blur() {
87870             change(true);
87871           }
87872
87873
87874           function change(skipWikidata) {
87875             var value = utilGetSetValue(_titleInput);
87876             var m = value.match(/https?:\/\/([-a-z]+)\.wikipedia\.org\/(?:wiki|\1-[-a-z]+)\/([^#]+)(?:#(.+))?/);
87877             var langInfo = m && _dataWikipedia.find(function (d) { return m[1] === d[2]; });
87878             var syncTags = {};
87879
87880             if (langInfo) {
87881               var nativeLangName = langInfo[1];
87882               // Normalize title http://www.mediawiki.org/wiki/API:Query#Title_normalization
87883               value = decodeURIComponent(m[2]).replace(/_/g, ' ');
87884               if (m[3]) {
87885                 var anchor;
87886                 // try {
87887                 // leave this out for now - #6232
87888                   // Best-effort `anchordecode:` implementation
87889                   // anchor = decodeURIComponent(m[3].replace(/\.([0-9A-F]{2})/g, '%$1'));
87890                 // } catch (e) {
87891                 anchor = decodeURIComponent(m[3]);
87892                 // }
87893                 value += '#' + anchor.replace(/_/g, ' ');
87894               }
87895               value = value.slice(0, 1).toUpperCase() + value.slice(1);
87896               utilGetSetValue(_langInput, nativeLangName);
87897               utilGetSetValue(_titleInput, value);
87898             }
87899
87900             if (value) {
87901               syncTags.wikipedia = context.cleanTagValue(language()[2] + ':' + value);
87902             } else {
87903               syncTags.wikipedia = undefined;
87904             }
87905
87906             dispatch$1.call('change', this, syncTags);
87907
87908
87909             if (skipWikidata || !value || !language()[2]) { return; }
87910
87911             // attempt asynchronous update of wikidata tag..
87912             var initGraph = context.graph();
87913             var initEntityIDs = _entityIDs;
87914
87915             wikidata.itemsByTitle(language()[2], value, function (err, data) {
87916               if (err || !data || !Object.keys(data).length) { return; }
87917
87918               // If graph has changed, we can't apply this update.
87919               if (context.graph() !== initGraph) { return; }
87920
87921               var qids = Object.keys(data);
87922               var value = qids && qids.find(function (id) { return id.match(/^Q\d+$/); });
87923
87924               var actions = initEntityIDs.map(function (entityID) {
87925                 var entity = context.entity(entityID).tags;
87926                 var currTags = Object.assign({}, entity);  // shallow copy
87927                 if (currTags.wikidata !== value) {
87928                     currTags.wikidata = value;
87929                     return actionChangeTags(entityID, currTags);
87930                 }
87931               }).filter(Boolean);
87932
87933               if (!actions.length) { return; }
87934
87935               // Coalesce the update of wikidata tag into the previous tag change
87936               context.overwrite(
87937                 function actionUpdateWikidataTags(graph) {
87938                   actions.forEach(function(action) {
87939                     graph = action(graph);
87940                   });
87941                   return graph;
87942                 },
87943                 context.history().undoAnnotation()
87944               );
87945
87946               // do not dispatch.call('change') here, because entity_editor
87947               // changeTags() is not intended to be called asynchronously
87948             });
87949           }
87950
87951
87952           wiki.tags = function (tags) {
87953             _tags = tags;
87954             updateForTags(tags);
87955           };
87956
87957           function updateForTags(tags) {
87958
87959             var value = typeof tags[field.key] === 'string' ? tags[field.key] : '';
87960             // Expect tag format of `tagLang:tagArticleTitle`, e.g. `fr:Paris`, with
87961             // optional suffix of `#anchor`
87962             var m = value.match(/([^:]+):([^#]+)(?:#(.+))?/);
87963             var tagLang = m && m[1];
87964             var tagArticleTitle = m && m[2];
87965             var anchor = m && m[3];
87966             var tagLangInfo = tagLang && _dataWikipedia.find(function (d) { return tagLang === d[2]; });
87967
87968             // value in correct format
87969             if (tagLangInfo) {
87970               var nativeLangName = tagLangInfo[1];
87971               utilGetSetValue(_langInput, nativeLangName);
87972               utilGetSetValue(_titleInput, tagArticleTitle + (anchor ? ('#' + anchor) : ''));
87973               if (anchor) {
87974                 try {
87975                   // Best-effort `anchorencode:` implementation
87976                   anchor = encodeURIComponent(anchor.replace(/ /g, '_')).replace(/%/g, '.');
87977                 } catch (e) {
87978                   anchor = anchor.replace(/ /g, '_');
87979                 }
87980               }
87981               _wikiURL = 'https://' + tagLang + '.wikipedia.org/wiki/' +
87982                 tagArticleTitle.replace(/ /g, '_') + (anchor ? ('#' + anchor) : '');
87983
87984             // unrecognized value format
87985             } else {
87986               utilGetSetValue(_titleInput, value);
87987               if (value && value !== '') {
87988                 utilGetSetValue(_langInput, '');
87989                 var defaultLangInfo = defaultLanguageInfo();
87990                 _wikiURL = "https://" + (defaultLangInfo[2]) + ".wikipedia.org/w/index.php?fulltext=1&search=" + value;
87991               } else {
87992                 var shownOrDefaultLangInfo = language(true /* skipEnglishFallback */);
87993                 utilGetSetValue(_langInput, shownOrDefaultLangInfo[1]);
87994                 _wikiURL = '';
87995               }
87996             }
87997           }
87998
87999
88000           wiki.entityIDs = function (val) {
88001             if (!arguments$1.length) { return _entityIDs; }
88002             _entityIDs = val;
88003             return wiki;
88004           };
88005
88006
88007           wiki.focus = function () {
88008             _titleInput.node().focus();
88009           };
88010
88011
88012           return utilRebind(wiki, dispatch$1, 'on');
88013         }
88014
88015         uiFieldWikipedia.supportsMultiselection = false;
88016
88017         var uiFields = {
88018             access: uiFieldAccess,
88019             address: uiFieldAddress,
88020             check: uiFieldCheck,
88021             combo: uiFieldCombo,
88022             cycleway: uiFieldCycleway,
88023             defaultCheck: uiFieldCheck,
88024             email: uiFieldText,
88025             identifier: uiFieldText,
88026             lanes: uiFieldLanes,
88027             localized: uiFieldLocalized,
88028             maxspeed: uiFieldMaxspeed,
88029             multiCombo: uiFieldCombo,
88030             networkCombo: uiFieldCombo,
88031             number: uiFieldText,
88032             onewayCheck: uiFieldCheck,
88033             radio: uiFieldRadio,
88034             restrictions: uiFieldRestrictions,
88035             semiCombo: uiFieldCombo,
88036             structureRadio: uiFieldRadio,
88037             tel: uiFieldText,
88038             text: uiFieldText,
88039             textarea: uiFieldTextarea,
88040             typeCombo: uiFieldCombo,
88041             url: uiFieldText,
88042             wikidata: uiFieldWikidata,
88043             wikipedia: uiFieldWikipedia
88044         };
88045
88046         function uiField(context, presetField, entityIDs, options) {
88047             options = Object.assign({
88048                 show: true,
88049                 wrap: true,
88050                 remove: true,
88051                 revert: true,
88052                 info: true
88053             }, options);
88054
88055             var dispatch$1 = dispatch('change', 'revert');
88056             var field = Object.assign({}, presetField);   // shallow copy
88057             field.domId = utilUniqueDomId('form-field-' + field.safeid);
88058             var _show = options.show;
88059             var _state = '';
88060             var _tags = {};
88061
88062             var _locked = false;
88063             var _lockedTip = uiTooltip()
88064                 .title(_t('inspector.lock.suggestion', { label: field.label }))
88065                 .placement('bottom');
88066
88067
88068             field.keys = field.keys || [field.key];
88069
88070             // only create the fields that are actually being shown
88071             if (_show && !field.impl) {
88072                 createField();
88073             }
88074
88075             // Creates the field.. This is done lazily,
88076             // once we know that the field will be shown.
88077             function createField() {
88078                 field.impl = uiFields[field.type](field, context)
88079                     .on('change', function(t, onInput) {
88080                         dispatch$1.call('change', field, t, onInput);
88081                     });
88082
88083                 if (entityIDs) {
88084                     field.entityIDs = entityIDs;
88085                     // if this field cares about the entities, pass them along
88086                     if (field.impl.entityIDs) {
88087                         field.impl.entityIDs(entityIDs);
88088                     }
88089                 }
88090             }
88091
88092
88093             function isModified() {
88094                 if (!entityIDs || !entityIDs.length) { return false; }
88095                 return entityIDs.some(function(entityID) {
88096                     var original = context.graph().base().entities[entityID];
88097                     var latest = context.graph().entity(entityID);
88098                     return field.keys.some(function(key) {
88099                         return original ? latest.tags[key] !== original.tags[key] : latest.tags[key];
88100                     });
88101                 });
88102             }
88103
88104
88105             function tagsContainFieldKey() {
88106                 return field.keys.some(function(key) {
88107                     if (field.type === 'multiCombo') {
88108                         for (var tagKey in _tags) {
88109                             if (tagKey.indexOf(key) === 0) {
88110                                 return true;
88111                             }
88112                         }
88113                         return false;
88114                     }
88115                     return _tags[key] !== undefined;
88116                 });
88117             }
88118
88119
88120             function revert(d) {
88121                 event.stopPropagation();
88122                 event.preventDefault();
88123                 if (!entityIDs || _locked) { return; }
88124
88125                 dispatch$1.call('revert', d, d.keys);
88126             }
88127
88128
88129             function remove(d) {
88130                 event.stopPropagation();
88131                 event.preventDefault();
88132                 if (_locked) { return; }
88133
88134                 var t = {};
88135                 d.keys.forEach(function(key) {
88136                     t[key] = undefined;
88137                 });
88138
88139                 dispatch$1.call('change', d, t);
88140             }
88141
88142
88143             field.render = function(selection) {
88144                 var container = selection.selectAll('.form-field')
88145                     .data([field]);
88146
88147                 // Enter
88148                 var enter = container.enter()
88149                     .append('div')
88150                     .attr('class', function(d) { return 'form-field form-field-' + d.safeid; })
88151                     .classed('nowrap', !options.wrap);
88152
88153                 if (options.wrap) {
88154                     var labelEnter = enter
88155                         .append('label')
88156                         .attr('class', 'field-label')
88157                         .attr('for', function(d) { return d.domId; });
88158
88159                     var textEnter = labelEnter
88160                         .append('span')
88161                         .attr('class', 'label-text');
88162
88163                     textEnter
88164                         .append('span')
88165                         .attr('class', 'label-textvalue')
88166                         .text(function(d) { return d.label(); });
88167
88168                     textEnter
88169                         .append('span')
88170                         .attr('class', 'label-textannotation');
88171
88172                     if (options.remove) {
88173                         labelEnter
88174                             .append('button')
88175                             .attr('class', 'remove-icon')
88176                             .attr('title', _t('icons.remove'))
88177                             .attr('tabindex', -1)
88178                             .call(svgIcon('#iD-operation-delete'));
88179                     }
88180
88181                     if (options.revert) {
88182                         labelEnter
88183                             .append('button')
88184                             .attr('class', 'modified-icon')
88185                             .attr('title', _t('icons.undo'))
88186                             .attr('tabindex', -1)
88187                             .call(svgIcon((_mainLocalizer.textDirection() === 'rtl') ? '#iD-icon-redo' : '#iD-icon-undo'));
88188                     }
88189                 }
88190
88191
88192                 // Update
88193                 container = container
88194                     .merge(enter);
88195
88196                 container.select('.field-label > .remove-icon')  // propagate bound data
88197                     .on('click', remove);
88198
88199                 container.select('.field-label > .modified-icon')  // propagate bound data
88200                     .on('click', revert);
88201
88202                 container
88203                     .each(function(d) {
88204                         var selection = select(this);
88205
88206                         if (!d.impl) {
88207                             createField();
88208                         }
88209
88210                         var reference, help;
88211
88212                         // instantiate field help
88213                         if (options.wrap && field.type === 'restrictions') {
88214                             help = uiFieldHelp(context, 'restrictions');
88215                         }
88216
88217                         // instantiate tag reference
88218                         if (options.wrap && options.info) {
88219                             var referenceKey = d.key;
88220                             if (d.type === 'multiCombo') {   // lookup key without the trailing ':'
88221                                 referenceKey = referenceKey.replace(/:$/, '');
88222                             }
88223
88224                             reference = uiTagReference(d.reference || { key: referenceKey });
88225                             if (_state === 'hover') {
88226                                 reference.showing(false);
88227                             }
88228                         }
88229
88230                         selection
88231                             .call(d.impl);
88232
88233                         // add field help components
88234                         if (help) {
88235                             selection
88236                                 .call(help.body)
88237                                 .select('.field-label')
88238                                 .call(help.button);
88239                         }
88240
88241                         // add tag reference components
88242                         if (reference) {
88243                             selection
88244                                 .call(reference.body)
88245                                 .select('.field-label')
88246                                 .call(reference.button);
88247                         }
88248
88249                         d.impl.tags(_tags);
88250                     });
88251
88252
88253                     container
88254                         .classed('locked', _locked)
88255                         .classed('modified', isModified())
88256                         .classed('present', tagsContainFieldKey());
88257
88258
88259                     // show a tip and lock icon if the field is locked
88260                     var annotation = container.selectAll('.field-label .label-textannotation');
88261                     var icon = annotation.selectAll('.icon')
88262                         .data(_locked ? [0]: []);
88263
88264                     icon.exit()
88265                         .remove();
88266
88267                     icon.enter()
88268                         .append('svg')
88269                         .attr('class', 'icon')
88270                         .append('use')
88271                         .attr('xlink:href', '#fas-lock');
88272
88273                     container.call(_locked ? _lockedTip : _lockedTip.destroy);
88274             };
88275
88276
88277             field.state = function(val) {
88278                 if (!arguments.length) { return _state; }
88279                 _state = val;
88280                 return field;
88281             };
88282
88283
88284             field.tags = function(val) {
88285                 if (!arguments.length) { return _tags; }
88286                 _tags = val;
88287
88288                 if (tagsContainFieldKey() && !_show) {
88289                     // always show a field if it has a value to display
88290                     _show = true;
88291                     if (!field.impl) {
88292                         createField();
88293                     }
88294                 }
88295
88296                 return field;
88297             };
88298
88299
88300             field.locked = function(val) {
88301                 if (!arguments.length) { return _locked; }
88302                 _locked = val;
88303                 return field;
88304             };
88305
88306
88307             field.show = function() {
88308                 _show = true;
88309                 if (!field.impl) {
88310                     createField();
88311                 }
88312                 if (field.default && field.key && _tags[field.key] !== field.default) {
88313                     var t = {};
88314                     t[field.key] = field.default;
88315                     dispatch$1.call('change', this, t);
88316                 }
88317             };
88318
88319             // A shown field has a visible UI, a non-shown field is in the 'Add field' dropdown
88320             field.isShown = function() {
88321                 return _show;
88322             };
88323
88324
88325             // An allowed field can appear in the UI or in the 'Add field' dropdown.
88326             // A non-allowed field is hidden from the user altogether
88327             field.isAllowed = function() {
88328
88329                 if (entityIDs &&
88330                     entityIDs.length > 1 &&
88331                     uiFields[field.type].supportsMultiselection === false) { return false; }
88332
88333                 if (field.geometry && !entityIDs.every(function(entityID) {
88334                     return field.matchGeometry(context.graph().geometry(entityID));
88335                 })) { return false; }
88336
88337                 if (field.countryCodes || field.notCountryCodes) {
88338                     var extent = combinedEntityExtent();
88339                     if (!extent) { return true; }
88340
88341                     var center = extent.center();
88342                     var countryCode = iso1A2Code(center);
88343
88344                     if (!countryCode) { return false; }
88345
88346                     countryCode = countryCode.toLowerCase();
88347
88348                     if (field.countryCodes && field.countryCodes.indexOf(countryCode) === -1) {
88349                         return false;
88350                     }
88351                     if (field.notCountryCodes && field.notCountryCodes.indexOf(countryCode) !== -1) {
88352                         return false;
88353                     }
88354                 }
88355
88356                 var prerequisiteTag = field.prerequisiteTag;
88357
88358                 if (entityIDs &&
88359                     !tagsContainFieldKey() && // ignore tagging prerequisites if a value is already present
88360                     prerequisiteTag) {
88361
88362                     if (!entityIDs.every(function(entityID) {
88363                         var entity = context.graph().entity(entityID);
88364                         if (prerequisiteTag.key) {
88365                             var value = entity.tags[prerequisiteTag.key];
88366                             if (!value) { return false; }
88367
88368                             if (prerequisiteTag.valueNot) {
88369                                 return prerequisiteTag.valueNot !== value;
88370                             }
88371                             if (prerequisiteTag.value) {
88372                                 return prerequisiteTag.value === value;
88373                             }
88374                         } else if (prerequisiteTag.keyNot) {
88375                             if (entity.tags[prerequisiteTag.keyNot]) { return false; }
88376                         }
88377                         return true;
88378                     })) { return false; }
88379                 }
88380
88381                 return true;
88382             };
88383
88384
88385             field.focus = function() {
88386                 if (field.impl) {
88387                     field.impl.focus();
88388                 }
88389             };
88390
88391
88392             function combinedEntityExtent() {
88393                 return entityIDs && entityIDs.length && entityIDs.reduce(function(extent, entityID) {
88394                     var entity = context.graph().entity(entityID);
88395                     return extent.extend(entity.extent(context.graph()));
88396                 }, geoExtent());
88397             }
88398
88399
88400             return utilRebind(field, dispatch$1, 'on');
88401         }
88402
88403         function uiFormFields(context) {
88404             var moreCombo = uiCombobox(context, 'more-fields').minItems(1);
88405             var _fieldsArr = [];
88406             var _lastPlaceholder = '';
88407             var _state = '';
88408             var _klass = '';
88409
88410
88411             function formFields(selection) {
88412                 var allowedFields = _fieldsArr.filter(function(field) { return field.isAllowed(); });
88413                 var shown = allowedFields.filter(function(field) { return field.isShown(); });
88414                 var notShown = allowedFields.filter(function(field) { return !field.isShown(); });
88415
88416                 var container = selection.selectAll('.form-fields-container')
88417                     .data([0]);
88418
88419                 container = container.enter()
88420                     .append('div')
88421                     .attr('class', 'form-fields-container ' + (_klass || ''))
88422                     .merge(container);
88423
88424
88425                 var fields = container.selectAll('.wrap-form-field')
88426                     .data(shown, function(d) { return d.id + (d.entityIDs ? d.entityIDs.join() : ''); });
88427
88428                 fields.exit()
88429                     .remove();
88430
88431                 // Enter
88432                 var enter = fields.enter()
88433                     .append('div')
88434                     .attr('class', function(d) { return 'wrap-form-field wrap-form-field-' + d.safeid; });
88435
88436                 // Update
88437                 fields = fields
88438                     .merge(enter);
88439
88440                 fields
88441                     .order()
88442                     .each(function(d) {
88443                         select(this)
88444                             .call(d.render);
88445                     });
88446
88447
88448                 var titles = [];
88449                 var moreFields = notShown.map(function(field) {
88450                     var label = field.label();
88451                     titles.push(label);
88452
88453                     var terms = field.terms();
88454                     if (field.key) { terms.push(field.key); }
88455                     if (field.keys) { terms = terms.concat(field.keys); }
88456
88457                     return {
88458                         title: label,
88459                         value: label,
88460                         field: field,
88461                         terms: terms
88462                     };
88463                 });
88464
88465                 var placeholder = titles.slice(0,3).join(', ') + ((titles.length > 3) ? '…' : '');
88466
88467
88468                 var more = selection.selectAll('.more-fields')
88469                     .data((_state === 'hover' || moreFields.length === 0) ? [] : [0]);
88470
88471                 more.exit()
88472                     .remove();
88473
88474                 var moreEnter = more.enter()
88475                     .append('div')
88476                     .attr('class', 'more-fields')
88477                     .append('label');
88478
88479                 moreEnter
88480                     .append('span')
88481                     .text(_t('inspector.add_fields'));
88482
88483                 more = moreEnter
88484                     .merge(more);
88485
88486
88487                 var input = more.selectAll('.value')
88488                     .data([0]);
88489
88490                 input.exit()
88491                     .remove();
88492
88493                 input = input.enter()
88494                     .append('input')
88495                     .attr('class', 'value')
88496                     .attr('type', 'text')
88497                     .attr('placeholder', placeholder)
88498                     .call(utilNoAuto)
88499                     .merge(input);
88500
88501                 input
88502                     .call(utilGetSetValue, '')
88503                     .call(moreCombo
88504                         .data(moreFields)
88505                         .on('accept', function (d) {
88506                             if (!d) { return; }  // user entered something that was not matched
88507                             var field = d.field;
88508                             field.show();
88509                             selection.call(formFields);  // rerender
88510                             if (field.type !== 'semiCombo' && field.type !== 'multiCombo') {
88511                                 field.focus();
88512                             }
88513                         })
88514                     );
88515
88516                 // avoid updating placeholder excessively (triggers style recalc)
88517                 if (_lastPlaceholder !== placeholder) {
88518                     input.attr('placeholder', placeholder);
88519                     _lastPlaceholder = placeholder;
88520                 }
88521             }
88522
88523
88524             formFields.fieldsArr = function(val) {
88525                 if (!arguments.length) { return _fieldsArr; }
88526                 _fieldsArr = val || [];
88527                 return formFields;
88528             };
88529
88530             formFields.state = function(val) {
88531                 if (!arguments.length) { return _state; }
88532                 _state = val;
88533                 return formFields;
88534             };
88535
88536             formFields.klass = function(val) {
88537                 if (!arguments.length) { return _klass; }
88538                 _klass = val;
88539                 return formFields;
88540             };
88541
88542
88543             return formFields;
88544         }
88545
88546         function uiSectionPresetFields(context) {
88547
88548             var section = uiSection('preset-fields', context)
88549                 .title(function() {
88550                     return _t('inspector.fields');
88551                 })
88552                 .disclosureContent(renderDisclosureContent);
88553
88554             var dispatch$1 = dispatch('change', 'revert');
88555             var formFields = uiFormFields(context);
88556             var _state;
88557             var _fieldsArr;
88558             var _presets = [];
88559             var _tags;
88560             var _entityIDs;
88561
88562             function renderDisclosureContent(selection) {
88563                 if (!_fieldsArr) {
88564
88565                     var graph = context.graph();
88566
88567                     var geometries = Object.keys(_entityIDs.reduce(function(geoms, entityID) {
88568                         geoms[graph.entity(entityID).geometry(graph)] = true;
88569                         return geoms;
88570                     }, {}));
88571
88572                     var presetsManager = _mainPresetIndex;
88573
88574                     var allFields = [];
88575                     var allMoreFields = [];
88576                     var sharedTotalFields;
88577
88578                     _presets.forEach(function(preset) {
88579                         var fields = preset.fields();
88580                         var moreFields = preset.moreFields();
88581
88582                         allFields = utilArrayUnion(allFields, fields);
88583                         allMoreFields = utilArrayUnion(allMoreFields, moreFields);
88584
88585                         if (!sharedTotalFields) {
88586                             sharedTotalFields = utilArrayUnion(fields, moreFields);
88587                         } else {
88588                             sharedTotalFields = sharedTotalFields.filter(function(field) {
88589                                 return fields.indexOf(field) !== -1 || moreFields.indexOf(field) !== -1;
88590                             });
88591                         }
88592                     });
88593
88594                     var sharedFields = allFields.filter(function(field) {
88595                         return sharedTotalFields.indexOf(field) !== -1;
88596                     });
88597                     var sharedMoreFields = allMoreFields.filter(function(field) {
88598                         return sharedTotalFields.indexOf(field) !== -1;
88599                     });
88600
88601                     _fieldsArr = [];
88602
88603                     sharedFields.forEach(function(field) {
88604                         if (field.matchAllGeometry(geometries)) {
88605                             _fieldsArr.push(
88606                                 uiField(context, field, _entityIDs)
88607                             );
88608                         }
88609                     });
88610
88611                     var singularEntity = _entityIDs.length === 1 && graph.hasEntity(_entityIDs[0]);
88612                     if (singularEntity && singularEntity.isHighwayIntersection(graph) && presetsManager.field('restrictions')) {
88613                         _fieldsArr.push(
88614                             uiField(context, presetsManager.field('restrictions'), _entityIDs)
88615                         );
88616                     }
88617
88618                     var additionalFields = utilArrayUnion(sharedMoreFields, presetsManager.universal());
88619                     additionalFields.sort(function(field1, field2) {
88620                         return field1.label().localeCompare(field2.label(), _mainLocalizer.localeCode());
88621                     });
88622
88623                     additionalFields.forEach(function(field) {
88624                         if (sharedFields.indexOf(field) === -1 &&
88625                             field.matchAllGeometry(geometries)) {
88626                             _fieldsArr.push(
88627                                 uiField(context, field, _entityIDs, { show: false })
88628                             );
88629                         }
88630                     });
88631
88632                     _fieldsArr.forEach(function(field) {
88633                         field
88634                             .on('change', function(t, onInput) {
88635                                 dispatch$1.call('change', field, _entityIDs, t, onInput);
88636                             })
88637                             .on('revert', function(keys) {
88638                                 dispatch$1.call('revert', field, keys);
88639                             });
88640                     });
88641                 }
88642
88643                 _fieldsArr.forEach(function(field) {
88644                     field
88645                         .state(_state)
88646                         .tags(_tags);
88647                 });
88648
88649
88650                 selection
88651                     .call(formFields
88652                         .fieldsArr(_fieldsArr)
88653                         .state(_state)
88654                         .klass('grouped-items-area')
88655                     );
88656
88657
88658                 selection.selectAll('.wrap-form-field input')
88659                     .on('keydown', function() {
88660                         // if user presses enter, and combobox is not active, accept edits..
88661                         if (event.keyCode === 13 && context.container().select('.combobox').empty()) {
88662                             context.enter(modeBrowse(context));
88663                         }
88664                     });
88665             }
88666
88667             section.presets = function(val) {
88668                 if (!arguments.length) { return _presets; }
88669                 if (!_presets || !val || !utilArrayIdentical(_presets, val)) {
88670                     _presets = val;
88671                     _fieldsArr = null;
88672                 }
88673                 return section;
88674             };
88675
88676             section.state = function(val) {
88677                 if (!arguments.length) { return _state; }
88678                 _state = val;
88679                 return section;
88680             };
88681
88682             section.tags = function(val) {
88683                 if (!arguments.length) { return _tags; }
88684                 _tags = val;
88685                 // Don't reset _fieldsArr here.
88686                 return section;
88687             };
88688
88689             section.entityIDs = function(val) {
88690                 if (!arguments.length) { return _entityIDs; }
88691                 if (!val || !_entityIDs || !utilArrayIdentical(_entityIDs, val)) {
88692                     _entityIDs = val;
88693                     _fieldsArr = null;
88694                 }
88695                 return section;
88696             };
88697
88698             return utilRebind(section, dispatch$1, 'on');
88699         }
88700
88701         function uiSectionRawMemberEditor(context) {
88702
88703             var section = uiSection('raw-member-editor', context)
88704                 .shouldDisplay(function() {
88705                     if (!_entityIDs || _entityIDs.length !== 1) { return false; }
88706
88707                     var entity = context.hasEntity(_entityIDs[0]);
88708                     return entity && entity.type === 'relation';
88709                 })
88710                 .title(function() {
88711                     var entity = context.hasEntity(_entityIDs[0]);
88712                     if (!entity) { return ''; }
88713
88714                     var gt = entity.members.length > _maxMembers ? '>' : '';
88715                     var count = gt + entity.members.slice(0, _maxMembers).length;
88716                     return _t('inspector.title_count', { title: _t('inspector.members'), count: count });
88717                 })
88718                 .disclosureContent(renderDisclosureContent);
88719
88720             var taginfo = services.taginfo;
88721             var _entityIDs;
88722             var _maxMembers = 1000;
88723
88724             function downloadMember(d) {
88725                 event.preventDefault();
88726
88727                 // display the loading indicator
88728                 select(this.parentNode).classed('tag-reference-loading', true);
88729                 context.loadEntity(d.id, function() {
88730                     section.reRender();
88731                 });
88732             }
88733
88734             function zoomToMember(d) {
88735                 event.preventDefault();
88736
88737                 var entity = context.entity(d.id);
88738                 context.map().zoomToEase(entity);
88739
88740                 // highlight the feature in case it wasn't previously on-screen
88741                 utilHighlightEntities([d.id], true, context);
88742             }
88743
88744
88745             function selectMember(d) {
88746                 event.preventDefault();
88747
88748                 // remove the hover-highlight styling
88749                 utilHighlightEntities([d.id], false, context);
88750
88751                 var entity = context.entity(d.id);
88752                 var mapExtent = context.map().extent();
88753                 if (!entity.intersects(mapExtent, context.graph())) {
88754                     // zoom to the entity if its extent is not visible now
88755                     context.map().zoomToEase(entity);
88756                 }
88757
88758                 context.enter(modeSelect(context, [d.id]));
88759             }
88760
88761
88762             function changeRole(d) {
88763                 var oldRole = d.role;
88764                 var newRole = context.cleanRelationRole(select(this).property('value'));
88765
88766                 if (oldRole !== newRole) {
88767                     var member = { id: d.id, type: d.type, role: newRole };
88768                     context.perform(
88769                         actionChangeMember(d.relation.id, member, d.index),
88770                         _t('operations.change_role.annotation')
88771                     );
88772                 }
88773             }
88774
88775
88776             function deleteMember(d) {
88777
88778                 // remove the hover-highlight styling
88779                 utilHighlightEntities([d.id], false, context);
88780
88781                 context.perform(
88782                     actionDeleteMember(d.relation.id, d.index),
88783                     _t('operations.delete_member.annotation')
88784                 );
88785
88786                 if (!context.hasEntity(d.relation.id)) {
88787                     context.enter(modeBrowse(context));
88788                 }
88789             }
88790
88791             function renderDisclosureContent(selection) {
88792
88793                 var entityID = _entityIDs[0];
88794
88795                 var memberships = [];
88796                 var entity = context.entity(entityID);
88797                 entity.members.slice(0, _maxMembers).forEach(function(member, index) {
88798                     memberships.push({
88799                         index: index,
88800                         id: member.id,
88801                         type: member.type,
88802                         role: member.role,
88803                         relation: entity,
88804                         member: context.hasEntity(member.id),
88805                         domId: utilUniqueDomId(entityID + '-member-' + index)
88806                     });
88807                 });
88808
88809                 var list = selection.selectAll('.member-list')
88810                     .data([0]);
88811
88812                 list = list.enter()
88813                     .append('ul')
88814                     .attr('class', 'member-list')
88815                     .merge(list);
88816
88817
88818                 var items = list.selectAll('li')
88819                     .data(memberships, function(d) {
88820                         return osmEntity.key(d.relation) + ',' + d.index + ',' +
88821                             (d.member ? osmEntity.key(d.member) : 'incomplete');
88822                     });
88823
88824                 items.exit()
88825                     .each(unbind)
88826                     .remove();
88827
88828                 var itemsEnter = items.enter()
88829                     .append('li')
88830                     .attr('class', 'member-row form-field')
88831                     .classed('member-incomplete', function(d) { return !d.member; });
88832
88833                 itemsEnter
88834                     .each(function(d) {
88835                         var item = select(this);
88836
88837                         var label = item
88838                             .append('label')
88839                             .attr('class', 'field-label')
88840                             .attr('for', d.domId);
88841
88842                         if (d.member) {
88843                             // highlight the member feature in the map while hovering on the list item
88844                             item
88845                                 .on('mouseover', function() {
88846                                     utilHighlightEntities([d.id], true, context);
88847                                 })
88848                                 .on('mouseout', function() {
88849                                     utilHighlightEntities([d.id], false, context);
88850                                 });
88851
88852                             var labelLink = label
88853                                 .append('span')
88854                                 .attr('class', 'label-text')
88855                                 .append('a')
88856                                 .attr('href', '#')
88857                                 .on('click', selectMember);
88858
88859                             labelLink
88860                                 .append('span')
88861                                 .attr('class', 'member-entity-type')
88862                                 .text(function(d) {
88863                                     var matched = _mainPresetIndex.match(d.member, context.graph());
88864                                     return (matched && matched.name()) || utilDisplayType(d.member.id);
88865                                 });
88866
88867                             labelLink
88868                                 .append('span')
88869                                 .attr('class', 'member-entity-name')
88870                                 .text(function(d) { return utilDisplayName(d.member); });
88871
88872                             label
88873                                 .append('button')
88874                                 .attr('tabindex', -1)
88875                                 .attr('title', _t('icons.remove'))
88876                                 .attr('class', 'remove member-delete')
88877                                 .call(svgIcon('#iD-operation-delete'));
88878
88879                             label
88880                                 .append('button')
88881                                 .attr('class', 'member-zoom')
88882                                 .attr('title', _t('icons.zoom_to'))
88883                                 .call(svgIcon('#iD-icon-framed-dot', 'monochrome'))
88884                                 .on('click', zoomToMember);
88885
88886                         } else {
88887                             var labelText = label
88888                                 .append('span')
88889                                 .attr('class', 'label-text');
88890
88891                             labelText
88892                                 .append('span')
88893                                 .attr('class', 'member-entity-type')
88894                                 .text(_t('inspector.' + d.type, { id: d.id }));
88895
88896                             labelText
88897                                 .append('span')
88898                                 .attr('class', 'member-entity-name')
88899                                 .text(_t('inspector.incomplete', { id: d.id }));
88900
88901                             label
88902                                 .append('button')
88903                                 .attr('class', 'member-download')
88904                                 .attr('title', _t('icons.download'))
88905                                 .attr('tabindex', -1)
88906                                 .call(svgIcon('#iD-icon-load'))
88907                                 .on('click', downloadMember);
88908                         }
88909                     });
88910
88911                 var wrapEnter = itemsEnter
88912                     .append('div')
88913                     .attr('class', 'form-field-input-wrap form-field-input-member');
88914
88915                 wrapEnter
88916                     .append('input')
88917                     .attr('class', 'member-role')
88918                     .attr('id', function(d) {
88919                         return d.domId;
88920                     })
88921                     .property('type', 'text')
88922                     .attr('placeholder', _t('inspector.role'))
88923                     .call(utilNoAuto);
88924
88925                 if (taginfo) {
88926                     wrapEnter.each(bindTypeahead);
88927                 }
88928
88929                 // update
88930                 items = items
88931                     .merge(itemsEnter)
88932                     .order();
88933
88934                 items.select('input.member-role')
88935                     .property('value', function(d) { return d.role; })
88936                     .on('blur', changeRole)
88937                     .on('change', changeRole);
88938
88939                 items.select('button.member-delete')
88940                     .on('click', deleteMember);
88941
88942                 var dragOrigin, targetIndex;
88943
88944                 items.call(d3_drag()
88945                     .on('start', function() {
88946                         dragOrigin = {
88947                             x: event.x,
88948                             y: event.y
88949                         };
88950                         targetIndex = null;
88951                     })
88952                     .on('drag', function(d, index) {
88953                         var x = event.x - dragOrigin.x,
88954                             y = event.y - dragOrigin.y;
88955
88956                         if (!select(this).classed('dragging') &&
88957                             // don't display drag until dragging beyond a distance threshold
88958                             Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)) <= 5) { return; }
88959
88960                         select(this)
88961                             .classed('dragging', true);
88962
88963                         targetIndex = null;
88964
88965                         selection.selectAll('li.member-row')
88966                             .style('transform', function(d2, index2) {
88967                                 var node = select(this).node();
88968                                 if (index === index2) {
88969                                     return 'translate(' + x + 'px, ' + y + 'px)';
88970                                 } else if (index2 > index && event.y > node.offsetTop) {
88971                                     if (targetIndex === null || index2 > targetIndex) {
88972                                         targetIndex = index2;
88973                                     }
88974                                     return 'translateY(-100%)';
88975                                 } else if (index2 < index && event.y < node.offsetTop + node.offsetHeight) {
88976                                     if (targetIndex === null || index2 < targetIndex) {
88977                                         targetIndex = index2;
88978                                     }
88979                                     return 'translateY(100%)';
88980                                 }
88981                                 return null;
88982                             });
88983                     })
88984                     .on('end', function(d, index) {
88985
88986                         if (!select(this).classed('dragging')) {
88987                             return;
88988                         }
88989
88990                         select(this)
88991                             .classed('dragging', false);
88992
88993                         selection.selectAll('li.member-row')
88994                             .style('transform', null);
88995
88996                         if (targetIndex !== null) {
88997                             // dragged to a new position, reorder
88998                             context.perform(
88999                                 actionMoveMember(d.relation.id, index, targetIndex),
89000                                 _t('operations.reorder_members.annotation')
89001                             );
89002                         }
89003                     })
89004                 );
89005
89006
89007
89008                 function bindTypeahead(d) {
89009                     var row = select(this);
89010                     var role = row.selectAll('input.member-role');
89011                     var origValue = role.property('value');
89012
89013                     function sort(value, data) {
89014                         var sameletter = [];
89015                         var other = [];
89016                         for (var i = 0; i < data.length; i++) {
89017                             if (data[i].value.substring(0, value.length) === value) {
89018                                 sameletter.push(data[i]);
89019                             } else {
89020                                 other.push(data[i]);
89021                             }
89022                         }
89023                         return sameletter.concat(other);
89024                     }
89025
89026                     role.call(uiCombobox(context, 'member-role')
89027                         .fetcher(function(role, callback) {
89028                             // The `geometry` param is used in the `taginfo.js` interface for
89029                             // filtering results, as a key into the `tag_members_fractions`
89030                             // object.  If we don't know the geometry because the member is
89031                             // not yet downloaded, it's ok to guess based on type.
89032                             var geometry;
89033                             if (d.member) {
89034                                 geometry = context.graph().geometry(d.member.id);
89035                             } else if (d.type === 'relation') {
89036                                 geometry = 'relation';
89037                             } else if (d.type === 'way') {
89038                                 geometry = 'line';
89039                             } else {
89040                                 geometry = 'point';
89041                             }
89042
89043                             var rtype = entity.tags.type;
89044                             taginfo.roles({
89045                                 debounce: true,
89046                                 rtype: rtype || '',
89047                                 geometry: geometry,
89048                                 query: role
89049                             }, function(err, data) {
89050                                 if (!err) { callback(sort(role, data)); }
89051                             });
89052                         })
89053                         .on('cancel', function() {
89054                             role.property('value', origValue);
89055                         })
89056                     );
89057                 }
89058
89059
89060                 function unbind() {
89061                     var row = select(this);
89062
89063                     row.selectAll('input.member-role')
89064                         .call(uiCombobox.off, context);
89065                 }
89066             }
89067
89068             section.entityIDs = function(val) {
89069                 if (!arguments.length) { return _entityIDs; }
89070                 _entityIDs = val;
89071                 return section;
89072             };
89073
89074
89075             return section;
89076         }
89077
89078         function uiSectionRawMembershipEditor(context) {
89079
89080             var section = uiSection('raw-membership-editor', context)
89081                 .shouldDisplay(function() {
89082                     return _entityIDs && _entityIDs.length === 1;
89083                 })
89084                 .title(function() {
89085                     var entity = context.hasEntity(_entityIDs[0]);
89086                     if (!entity) { return ''; }
89087
89088                     var parents = context.graph().parentRelations(entity);
89089                     var gt = parents.length > _maxMemberships ? '>' : '';
89090                     var count = gt + parents.slice(0, _maxMemberships).length;
89091                     return _t('inspector.title_count', { title: _t('inspector.relations'), count: count });
89092                 })
89093                 .disclosureContent(renderDisclosureContent);
89094
89095             var taginfo = services.taginfo;
89096             var nearbyCombo = uiCombobox(context, 'parent-relation')
89097                 .minItems(1)
89098                 .fetcher(fetchNearbyRelations)
89099                 .itemsMouseEnter(function(d) {
89100                     if (d.relation) { utilHighlightEntities([d.relation.id], true, context); }
89101                 })
89102                 .itemsMouseLeave(function(d) {
89103                     if (d.relation) { utilHighlightEntities([d.relation.id], false, context); }
89104                 });
89105             var _inChange = false;
89106             var _entityIDs = [];
89107             var _showBlank;
89108             var _maxMemberships = 1000;
89109
89110             function selectRelation(d) {
89111                 event.preventDefault();
89112
89113                 // remove the hover-highlight styling
89114                 utilHighlightEntities([d.relation.id], false, context);
89115
89116                 context.enter(modeSelect(context, [d.relation.id]));
89117             }
89118
89119             function zoomToRelation(d) {
89120                 event.preventDefault();
89121
89122                 var entity = context.entity(d.relation.id);
89123                 context.map().zoomToEase(entity);
89124
89125                 // highlight the relation in case it wasn't previously on-screen
89126                 utilHighlightEntities([d.relation.id], true, context);
89127             }
89128
89129
89130             function changeRole(d) {
89131                 if (d === 0) { return; }    // called on newrow (shouldn't happen)
89132                 if (_inChange) { return; }  // avoid accidental recursive call #5731
89133
89134                 var oldRole = d.member.role;
89135                 var newRole = context.cleanRelationRole(select(this).property('value'));
89136
89137                 if (oldRole !== newRole) {
89138                     _inChange = true;
89139                     context.perform(
89140                         actionChangeMember(d.relation.id, Object.assign({}, d.member, { role: newRole }), d.index),
89141                         _t('operations.change_role.annotation')
89142                     );
89143                 }
89144                 _inChange = false;
89145             }
89146
89147
89148             function addMembership(d, role) {
89149                 this.blur();           // avoid keeping focus on the button
89150                 _showBlank = false;
89151
89152                 var member = { id: _entityIDs[0], type: context.entity(_entityIDs[0]).type, role: role };
89153
89154                 if (d.relation) {
89155                     context.perform(
89156                         actionAddMember(d.relation.id, member),
89157                         _t('operations.add_member.annotation')
89158                     );
89159
89160                 } else {
89161                     var relation = osmRelation();
89162                     context.perform(
89163                         actionAddEntity(relation),
89164                         actionAddMember(relation.id, member),
89165                         _t('operations.add.annotation.relation')
89166                     );
89167
89168                     context.enter(modeSelect(context, [relation.id]).newFeature(true));
89169                 }
89170             }
89171
89172
89173             function deleteMembership(d) {
89174                 this.blur();           // avoid keeping focus on the button
89175                 if (d === 0) { return; }   // called on newrow (shouldn't happen)
89176
89177                 // remove the hover-highlight styling
89178                 utilHighlightEntities([d.relation.id], false, context);
89179
89180                 context.perform(
89181                     actionDeleteMember(d.relation.id, d.index),
89182                     _t('operations.delete_member.annotation')
89183                 );
89184             }
89185
89186
89187             function fetchNearbyRelations(q, callback) {
89188                 var newRelation = { relation: null, value: _t('inspector.new_relation') };
89189
89190                 var entityID = _entityIDs[0];
89191
89192                 var result = [];
89193
89194                 var graph = context.graph();
89195
89196                 function baseDisplayLabel(entity) {
89197                     var matched = _mainPresetIndex.match(entity, graph);
89198                     var presetName = (matched && matched.name()) || _t('inspector.relation');
89199                     var entityName = utilDisplayName(entity) || '';
89200
89201                     return presetName + ' ' + entityName;
89202                 }
89203
89204                 var explicitRelation = q && context.hasEntity(q.toLowerCase());
89205                 if (explicitRelation && explicitRelation.type === 'relation' && explicitRelation.id !== entityID) {
89206                     // loaded relation is specified explicitly, only show that
89207
89208                     result.push({
89209                         relation: explicitRelation,
89210                         value: baseDisplayLabel(explicitRelation) + ' ' + explicitRelation.id
89211                     });
89212                 } else {
89213
89214                     context.history().intersects(context.map().extent()).forEach(function(entity) {
89215                         if (entity.type !== 'relation' || entity.id === entityID) { return; }
89216
89217                         var value = baseDisplayLabel(entity);
89218                         if (q && (value + ' ' + entity.id).toLowerCase().indexOf(q.toLowerCase()) === -1) { return; }
89219
89220                         result.push({ relation: entity, value: value });
89221                     });
89222
89223                     result.sort(function(a, b) {
89224                         return osmRelation.creationOrder(a.relation, b.relation);
89225                     });
89226
89227                     // Dedupe identical names by appending relation id - see #2891
89228                     var dupeGroups = Object.values(utilArrayGroupBy(result, 'value'))
89229                         .filter(function(v) { return v.length > 1; });
89230
89231                     dupeGroups.forEach(function(group) {
89232                         group.forEach(function(obj) {
89233                             obj.value += ' ' + obj.relation.id;
89234                         });
89235                     });
89236                 }
89237
89238                 result.forEach(function(obj) {
89239                     obj.title = obj.value;
89240                 });
89241
89242                 result.unshift(newRelation);
89243                 callback(result);
89244             }
89245
89246             function renderDisclosureContent(selection) {
89247
89248                 var entityID = _entityIDs[0];
89249
89250                 var entity = context.entity(entityID);
89251                 var parents = context.graph().parentRelations(entity);
89252
89253                 var memberships = [];
89254
89255                 parents.slice(0, _maxMemberships).forEach(function(relation) {
89256                     relation.members.forEach(function(member, index) {
89257                         if (member.id === entity.id) {
89258                             memberships.push({
89259                                 relation: relation,
89260                                 member: member,
89261                                 index: index,
89262                                 domId: utilUniqueDomId(entityID + '-membership-' + relation.id + '-' + index)
89263                             });
89264                         }
89265                     });
89266                 });
89267
89268                 var list = selection.selectAll('.member-list')
89269                     .data([0]);
89270
89271                 list = list.enter()
89272                     .append('ul')
89273                     .attr('class', 'member-list')
89274                     .merge(list);
89275
89276
89277                 var items = list.selectAll('li.member-row-normal')
89278                     .data(memberships, function(d) {
89279                         return osmEntity.key(d.relation) + ',' + d.index;
89280                     });
89281
89282                 items.exit()
89283                     .each(unbind)
89284                     .remove();
89285
89286                 // Enter
89287                 var itemsEnter = items.enter()
89288                     .append('li')
89289                     .attr('class', 'member-row member-row-normal form-field');
89290
89291                 // highlight the relation in the map while hovering on the list item
89292                 itemsEnter.on('mouseover', function(d) {
89293                         utilHighlightEntities([d.relation.id], true, context);
89294                     })
89295                     .on('mouseout', function(d) {
89296                         utilHighlightEntities([d.relation.id], false, context);
89297                     });
89298
89299                 var labelEnter = itemsEnter
89300                     .append('label')
89301                     .attr('class', 'field-label')
89302                     .attr('for', function(d) {
89303                         return d.domId;
89304                     });
89305
89306                 var labelLink = labelEnter
89307                     .append('span')
89308                     .attr('class', 'label-text')
89309                     .append('a')
89310                     .attr('href', '#')
89311                     .on('click', selectRelation);
89312
89313                 labelLink
89314                     .append('span')
89315                     .attr('class', 'member-entity-type')
89316                     .text(function(d) {
89317                         var matched = _mainPresetIndex.match(d.relation, context.graph());
89318                         return (matched && matched.name()) || _t('inspector.relation');
89319                     });
89320
89321                 labelLink
89322                     .append('span')
89323                     .attr('class', 'member-entity-name')
89324                     .text(function(d) { return utilDisplayName(d.relation); });
89325
89326                 labelEnter
89327                     .append('button')
89328                     .attr('tabindex', -1)
89329                     .attr('class', 'remove member-delete')
89330                     .call(svgIcon('#iD-operation-delete'))
89331                     .on('click', deleteMembership);
89332
89333                 labelEnter
89334                     .append('button')
89335                     .attr('class', 'member-zoom')
89336                     .attr('title', _t('icons.zoom_to'))
89337                     .call(svgIcon('#iD-icon-framed-dot', 'monochrome'))
89338                     .on('click', zoomToRelation);
89339
89340                 var wrapEnter = itemsEnter
89341                     .append('div')
89342                     .attr('class', 'form-field-input-wrap form-field-input-member');
89343
89344                 wrapEnter
89345                     .append('input')
89346                     .attr('class', 'member-role')
89347                     .attr('id', function(d) {
89348                         return d.domId;
89349                     })
89350                     .property('type', 'text')
89351                     .attr('placeholder', _t('inspector.role'))
89352                     .call(utilNoAuto)
89353                     .property('value', function(d) { return d.member.role; })
89354                     .on('blur', changeRole)
89355                     .on('change', changeRole);
89356
89357                 if (taginfo) {
89358                     wrapEnter.each(bindTypeahead);
89359                 }
89360
89361
89362                 var newMembership = list.selectAll('.member-row-new')
89363                     .data(_showBlank ? [0] : []);
89364
89365                 // Exit
89366                 newMembership.exit()
89367                     .remove();
89368
89369                 // Enter
89370                 var newMembershipEnter = newMembership.enter()
89371                     .append('li')
89372                     .attr('class', 'member-row member-row-new form-field');
89373
89374                 var newLabelEnter = newMembershipEnter
89375                     .append('label')
89376                     .attr('class', 'field-label');
89377
89378                 newLabelEnter
89379                     .append('input')
89380                     .attr('placeholder', _t('inspector.choose_relation'))
89381                     .attr('type', 'text')
89382                     .attr('class', 'member-entity-input')
89383                     .call(utilNoAuto);
89384
89385                 newLabelEnter
89386                     .append('button')
89387                     .attr('tabindex', -1)
89388                     .attr('class', 'remove member-delete')
89389                     .call(svgIcon('#iD-operation-delete'))
89390                     .on('click', function() {
89391                         list.selectAll('.member-row-new')
89392                             .remove();
89393                     });
89394
89395                 var newWrapEnter = newMembershipEnter
89396                     .append('div')
89397                     .attr('class', 'form-field-input-wrap form-field-input-member');
89398
89399                 newWrapEnter
89400                     .append('input')
89401                     .attr('class', 'member-role')
89402                     .property('type', 'text')
89403                     .attr('placeholder', _t('inspector.role'))
89404                     .call(utilNoAuto);
89405
89406                 // Update
89407                 newMembership = newMembership
89408                     .merge(newMembershipEnter);
89409
89410                 newMembership.selectAll('.member-entity-input')
89411                     .on('blur', cancelEntity)   // if it wasn't accepted normally, cancel it
89412                     .call(nearbyCombo
89413                         .on('accept', acceptEntity)
89414                         .on('cancel', cancelEntity)
89415                     );
89416
89417
89418                 // Container for the Add button
89419                 var addRow = selection.selectAll('.add-row')
89420                     .data([0]);
89421
89422                 // enter
89423                 var addRowEnter = addRow.enter()
89424                     .append('div')
89425                     .attr('class', 'add-row');
89426
89427                 var addRelationButton = addRowEnter
89428                     .append('button')
89429                     .attr('class', 'add-relation');
89430
89431                 addRelationButton
89432                     .call(svgIcon('#iD-icon-plus', 'light'));
89433                 addRelationButton
89434                     .call(uiTooltip().title(_t('inspector.add_to_relation')).placement(_mainLocalizer.textDirection() === 'ltr' ? 'right' : 'left'));
89435
89436                 addRowEnter
89437                     .append('div')
89438                     .attr('class', 'space-value');   // preserve space
89439
89440                 addRowEnter
89441                     .append('div')
89442                     .attr('class', 'space-buttons');  // preserve space
89443
89444                 // update
89445                 addRow = addRow
89446                     .merge(addRowEnter);
89447
89448                 addRow.select('.add-relation')
89449                     .on('click', function() {
89450                         _showBlank = true;
89451                         section.reRender();
89452                         list.selectAll('.member-entity-input').node().focus();
89453                     });
89454
89455
89456                 function acceptEntity(d) {
89457                     if (!d) {
89458                         cancelEntity();
89459                         return;
89460                     }
89461                     // remove hover-higlighting
89462                     if (d.relation) { utilHighlightEntities([d.relation.id], false, context); }
89463
89464                     var role = context.cleanRelationRole(list.selectAll('.member-row-new .member-role').property('value'));
89465                     addMembership(d, role);
89466                 }
89467
89468
89469                 function cancelEntity() {
89470                     var input = newMembership.selectAll('.member-entity-input');
89471                     input.property('value', '');
89472
89473                     // remove hover-higlighting
89474                     context.surface().selectAll('.highlighted')
89475                         .classed('highlighted', false);
89476                 }
89477
89478
89479                 function bindTypeahead(d) {
89480                     var row = select(this);
89481                     var role = row.selectAll('input.member-role');
89482                     var origValue = role.property('value');
89483
89484                     function sort(value, data) {
89485                         var sameletter = [];
89486                         var other = [];
89487                         for (var i = 0; i < data.length; i++) {
89488                             if (data[i].value.substring(0, value.length) === value) {
89489                                 sameletter.push(data[i]);
89490                             } else {
89491                                 other.push(data[i]);
89492                             }
89493                         }
89494                         return sameletter.concat(other);
89495                     }
89496
89497                     role.call(uiCombobox(context, 'member-role')
89498                         .fetcher(function(role, callback) {
89499                             var rtype = d.relation.tags.type;
89500                             taginfo.roles({
89501                                 debounce: true,
89502                                 rtype: rtype || '',
89503                                 geometry: context.graph().geometry(entityID),
89504                                 query: role
89505                             }, function(err, data) {
89506                                 if (!err) { callback(sort(role, data)); }
89507                             });
89508                         })
89509                         .on('cancel', function() {
89510                             role.property('value', origValue);
89511                         })
89512                     );
89513                 }
89514
89515
89516                 function unbind() {
89517                     var row = select(this);
89518
89519                     row.selectAll('input.member-role')
89520                         .call(uiCombobox.off, context);
89521                 }
89522             }
89523
89524
89525             section.entityIDs = function(val) {
89526                 if (!arguments.length) { return _entityIDs; }
89527                 _entityIDs = val;
89528                 _showBlank = false;
89529                 return section;
89530             };
89531
89532
89533             return section;
89534         }
89535
89536         function uiSectionSelectionList(context) {
89537
89538             var _selectedIDs = [];
89539
89540             var section = uiSection('selected-features', context)
89541                 .shouldDisplay(function() {
89542                     return _selectedIDs.length > 1;
89543                 })
89544                 .title(function() {
89545                     return _t('inspector.title_count', { title: _t('inspector.features'), count: _selectedIDs.length });
89546                 })
89547                 .disclosureContent(renderDisclosureContent);
89548
89549             context.history()
89550                 .on('change.selectionList', function(difference) {
89551                     if (difference) {
89552                         section.reRender();
89553                     }
89554                 });
89555
89556             section.entityIDs = function(val) {
89557                 if (!arguments.length) { return _selectedIDs; }
89558                 _selectedIDs = val;
89559                 return section;
89560             };
89561
89562             function selectEntity(entity) {
89563                 context.enter(modeSelect(context, [entity.id]));
89564             }
89565
89566             function deselectEntity(entity) {
89567                 event.stopPropagation();
89568
89569                 var selectedIDs = _selectedIDs.slice();
89570                 var index = selectedIDs.indexOf(entity.id);
89571                 if (index > -1) {
89572                     selectedIDs.splice(index, 1);
89573                     context.enter(modeSelect(context, selectedIDs));
89574                 }
89575             }
89576
89577             function renderDisclosureContent(selection) {
89578
89579                 var list = selection.selectAll('.feature-list')
89580                     .data([0]);
89581
89582                 list = list.enter()
89583                     .append('div')
89584                     .attr('class', 'feature-list')
89585                     .merge(list);
89586
89587                 var entities = _selectedIDs
89588                     .map(function(id) { return context.hasEntity(id); })
89589                     .filter(Boolean);
89590
89591                 var items = list.selectAll('.feature-list-item')
89592                     .data(entities, osmEntity.key);
89593
89594                 items.exit()
89595                     .remove();
89596
89597                 // Enter
89598                 var enter = items.enter()
89599                     .append('div')
89600                     .attr('class', 'feature-list-item')
89601                     .on('click', selectEntity);
89602
89603                 enter
89604                     .each(function(d) {
89605                         select(this).on('mouseover', function() {
89606                             utilHighlightEntities([d.id], true, context);
89607                         });
89608                         select(this).on('mouseout', function() {
89609                             utilHighlightEntities([d.id], false, context);
89610                         });
89611                     });
89612
89613                 var label = enter
89614                     .append('button')
89615                     .attr('class', 'label');
89616
89617                 enter
89618                     .append('button')
89619                     .attr('class', 'close')
89620                     .attr('title', _t('icons.deselect'))
89621                     .on('click', deselectEntity)
89622                     .call(svgIcon('#iD-icon-close'));
89623
89624                 label
89625                     .append('span')
89626                     .attr('class', 'entity-geom-icon')
89627                     .call(svgIcon('', 'pre-text'));
89628
89629                 label
89630                     .append('span')
89631                     .attr('class', 'entity-type');
89632
89633                 label
89634                     .append('span')
89635                     .attr('class', 'entity-name');
89636
89637                 // Update
89638                 items = items.merge(enter);
89639
89640                 items.selectAll('.entity-geom-icon use')
89641                     .attr('href', function() {
89642                         var entity = this.parentNode.parentNode.__data__;
89643                         return '#iD-icon-' + entity.geometry(context.graph());
89644                     });
89645
89646                 items.selectAll('.entity-type')
89647                     .text(function(entity) { return _mainPresetIndex.match(entity, context.graph()).name(); });
89648
89649                 items.selectAll('.entity-name')
89650                     .text(function(d) {
89651                         // fetch latest entity
89652                         var entity = context.entity(d.id);
89653                         return utilDisplayName(entity);
89654                     });
89655             }
89656
89657             return section;
89658         }
89659
89660         function uiEntityEditor(context) {
89661             var dispatch$1 = dispatch('choose');
89662             var _state = 'select';
89663             var _coalesceChanges = false;
89664             var _modified = false;
89665             var _base;
89666             var _entityIDs;
89667             var _activePresets = [];
89668             var _newFeature;
89669
89670             var _sections;
89671
89672             function entityEditor(selection) {
89673
89674                 var combinedTags = utilCombinedTags(_entityIDs, context.graph());
89675
89676                 // Header
89677                 var header = selection.selectAll('.header')
89678                     .data([0]);
89679
89680                 // Enter
89681                 var headerEnter = header.enter()
89682                     .append('div')
89683                     .attr('class', 'header fillL cf');
89684
89685                 headerEnter
89686                     .append('button')
89687                     .attr('class', 'preset-reset preset-choose')
89688                     .call(svgIcon((_mainLocalizer.textDirection() === 'rtl') ? '#iD-icon-forward' : '#iD-icon-backward'));
89689
89690                 headerEnter
89691                     .append('button')
89692                     .attr('class', 'close')
89693                     .on('click', function() { context.enter(modeBrowse(context)); })
89694                     .call(svgIcon(_modified ? '#iD-icon-apply' : '#iD-icon-close'));
89695
89696                 headerEnter
89697                     .append('h3');
89698
89699                 // Update
89700                 header = header
89701                     .merge(headerEnter);
89702
89703                 header.selectAll('h3')
89704                     .text(_entityIDs.length === 1 ? _t('inspector.edit') : _t('inspector.edit_features'));
89705
89706                 header.selectAll('.preset-reset')
89707                     .on('click', function() {
89708                         dispatch$1.call('choose', this, _activePresets);
89709                     });
89710
89711                 // Body
89712                 var body = selection.selectAll('.inspector-body')
89713                     .data([0]);
89714
89715                 // Enter
89716                 var bodyEnter = body.enter()
89717                     .append('div')
89718                     .attr('class', 'entity-editor inspector-body sep-top');
89719
89720                 // Update
89721                 body = body
89722                     .merge(bodyEnter);
89723
89724                 if (!_sections) {
89725                     _sections = [
89726                         uiSectionSelectionList(context),
89727                         uiSectionFeatureType(context).on('choose', function(presets) {
89728                             dispatch$1.call('choose', this, presets);
89729                         }),
89730                         uiSectionEntityIssues(context),
89731                         uiSectionPresetFields(context).on('change', changeTags).on('revert', revertTags),
89732                         uiSectionRawTagEditor('raw-tag-editor', context).on('change', changeTags),
89733                         uiSectionRawMemberEditor(context),
89734                         uiSectionRawMembershipEditor(context)
89735                     ];
89736                 }
89737
89738                 _sections.forEach(function(section) {
89739                     if (section.entityIDs) {
89740                         section.entityIDs(_entityIDs);
89741                     }
89742                     if (section.presets) {
89743                         section.presets(_activePresets);
89744                     }
89745                     if (section.tags) {
89746                         section.tags(combinedTags);
89747                     }
89748                     if (section.state) {
89749                         section.state(_state);
89750                     }
89751                     body.call(section.render);
89752                 });
89753
89754                 body
89755                     .selectAll('.key-trap-wrap')
89756                     .data([0])
89757                     .enter()
89758                     .append('div')
89759                     .attr('class', 'key-trap-wrap')
89760                     .append('input')
89761                     .attr('type', 'text')
89762                     .attr('class', 'key-trap')
89763                     .on('keydown.key-trap', function() {
89764                         // On tabbing, send focus back to the first field on the inspector-body
89765                         // (probably the `name` field) #4159
89766                         if (event.keyCode === 9 && !event.shiftKey) {
89767                             event.preventDefault();
89768                             body.select('input').node().focus();
89769                         }
89770                     });
89771
89772                 context.history()
89773                     .on('change.entity-editor', historyChanged);
89774
89775                 function historyChanged(difference) {
89776                     if (selection.selectAll('.entity-editor').empty()) { return; }
89777                     if (_state === 'hide') { return; }
89778                     var significant = !difference ||
89779                             difference.didChange.properties ||
89780                             difference.didChange.addition ||
89781                             difference.didChange.deletion;
89782                     if (!significant) { return; }
89783
89784                     _entityIDs = _entityIDs.filter(context.hasEntity);
89785                     if (!_entityIDs.length) { return; }
89786
89787                     var priorActivePreset = _activePresets.length === 1 && _activePresets[0];
89788
89789                     loadActivePresets();
89790
89791                     var graph = context.graph();
89792                     entityEditor.modified(_base !== graph);
89793                     entityEditor(selection);
89794
89795                     if (priorActivePreset && _activePresets.length === 1 && priorActivePreset !== _activePresets[0]) {
89796                         // flash the button to indicate the preset changed
89797                         context.container().selectAll('.entity-editor button.preset-reset .label')
89798                             .style('background-color', '#fff')
89799                             .transition()
89800                             .duration(750)
89801                             .style('background-color', null);
89802                     }
89803                 }
89804             }
89805
89806
89807             // Tag changes that fire on input can all get coalesced into a single
89808             // history operation when the user leaves the field.  #2342
89809             // Use explicit entityIDs in case the selection changes before the event is fired.
89810             function changeTags(entityIDs, changed, onInput) {
89811
89812                 var actions = [];
89813                 for (var i in entityIDs) {
89814                     var entityID = entityIDs[i];
89815                     var entity = context.entity(entityID);
89816
89817                     var tags = Object.assign({}, entity.tags);   // shallow copy
89818
89819                     for (var k in changed) {
89820                         if (!k) { continue; }
89821                         var v = changed[k];
89822                         if (v !== undefined || tags.hasOwnProperty(k)) {
89823                             tags[k] = v;
89824                         }
89825                     }
89826
89827                     if (!onInput) {
89828                         tags = utilCleanTags(tags);
89829                     }
89830
89831                     if (!fastDeepEqual(entity.tags, tags)) {
89832                         actions.push(actionChangeTags(entityID, tags));
89833                     }
89834                 }
89835
89836                 if (actions.length) {
89837                     var combinedAction = function(graph) {
89838                         actions.forEach(function(action) {
89839                             graph = action(graph);
89840                         });
89841                         return graph;
89842                     };
89843
89844                     var annotation = _t('operations.change_tags.annotation');
89845
89846                     if (_coalesceChanges) {
89847                         context.overwrite(combinedAction, annotation);
89848                     } else {
89849                         context.perform(combinedAction, annotation);
89850                         _coalesceChanges = !!onInput;
89851                     }
89852                 }
89853
89854                 // if leaving field (blur event), rerun validation
89855                 if (!onInput) {
89856                     context.validator().validate();
89857                 }
89858             }
89859
89860             function revertTags(keys) {
89861
89862                 var actions = [];
89863                 for (var i in _entityIDs) {
89864                     var entityID = _entityIDs[i];
89865
89866                     var original = context.graph().base().entities[entityID];
89867                     var changed = {};
89868                     for (var j in keys) {
89869                         var key = keys[j];
89870                         changed[key] = original ? original.tags[key] : undefined;
89871                     }
89872
89873                     var entity = context.entity(entityID);
89874                     var tags = Object.assign({}, entity.tags);   // shallow copy
89875
89876                     for (var k in changed) {
89877                         if (!k) { continue; }
89878                         var v = changed[k];
89879                         if (v !== undefined || tags.hasOwnProperty(k)) {
89880                             tags[k] = v;
89881                         }
89882                     }
89883
89884
89885                     tags = utilCleanTags(tags);
89886
89887                     if (!fastDeepEqual(entity.tags, tags)) {
89888                         actions.push(actionChangeTags(entityID, tags));
89889                     }
89890
89891                 }
89892
89893                 if (actions.length) {
89894                     var combinedAction = function(graph) {
89895                         actions.forEach(function(action) {
89896                             graph = action(graph);
89897                         });
89898                         return graph;
89899                     };
89900
89901                     var annotation = _t('operations.change_tags.annotation');
89902
89903                     if (_coalesceChanges) {
89904                         context.overwrite(combinedAction, annotation);
89905                     } else {
89906                         context.perform(combinedAction, annotation);
89907                         _coalesceChanges = false;
89908                     }
89909                 }
89910
89911                 context.validator().validate();
89912             }
89913
89914
89915             entityEditor.modified = function(val) {
89916                 if (!arguments.length) { return _modified; }
89917                 _modified = val;
89918                 return entityEditor;
89919             };
89920
89921
89922             entityEditor.state = function(val) {
89923                 if (!arguments.length) { return _state; }
89924                 _state = val;
89925                 return entityEditor;
89926             };
89927
89928
89929             entityEditor.entityIDs = function(val) {
89930                 if (!arguments.length) { return _entityIDs; }
89931                 if (val && _entityIDs && utilArrayIdentical(_entityIDs, val)) { return entityEditor; }  // exit early if no change
89932
89933                 _entityIDs = val;
89934                 _base = context.graph();
89935                 _coalesceChanges = false;
89936
89937                 loadActivePresets(true);
89938
89939                 return entityEditor
89940                     .modified(false);
89941             };
89942
89943
89944             entityEditor.newFeature = function(val) {
89945                 if (!arguments.length) { return _newFeature; }
89946                 _newFeature = val;
89947                 return entityEditor;
89948             };
89949
89950
89951             function loadActivePresets(isForNewSelection) {
89952
89953                 var graph = context.graph();
89954
89955                 var counts = {};
89956
89957                 for (var i in _entityIDs) {
89958                     var entity = graph.hasEntity(_entityIDs[i]);
89959                     if (!entity) { return; }
89960
89961                     var match = _mainPresetIndex.match(entity, graph);
89962
89963                     if (!counts[match.id]) { counts[match.id] = 0; }
89964                     counts[match.id] += 1;
89965                 }
89966
89967                 var matches = Object.keys(counts).sort(function(p1, p2) {
89968                     return counts[p2] - counts[p1];
89969                 }).map(function(pID) {
89970                     return _mainPresetIndex.item(pID);
89971                 });
89972
89973                 if (!isForNewSelection) {
89974                     // A "weak" preset doesn't set any tags. (e.g. "Address")
89975                     var weakPreset = _activePresets.length === 1 &&
89976                         !_activePresets[0].isFallback() &&
89977                         Object.keys(_activePresets[0].addTags || {}).length === 0;
89978                     // Don't replace a weak preset with a fallback preset (e.g. "Point")
89979                     if (weakPreset && matches.length === 1 && matches[0].isFallback()) { return; }
89980                 }
89981
89982                 entityEditor.presets(matches);
89983             }
89984
89985             entityEditor.presets = function(val) {
89986                 if (!arguments.length) { return _activePresets; }
89987
89988                 // don't reload the same preset
89989                 if (!utilArrayIdentical(val, _activePresets)) {
89990                     _activePresets = val;
89991                 }
89992                 return entityEditor;
89993             };
89994
89995             return utilRebind(entityEditor, dispatch$1, 'on');
89996         }
89997
89998         function uiPresetList(context) {
89999             var dispatch$1 = dispatch('cancel', 'choose');
90000             var _entityIDs;
90001             var _currentPresets;
90002             var _autofocus = false;
90003
90004
90005             function presetList(selection) {
90006                 if (!_entityIDs) { return; }
90007
90008                 var presets = _mainPresetIndex.matchAllGeometry(entityGeometries());
90009
90010                 selection.html('');
90011
90012                 var messagewrap = selection
90013                     .append('div')
90014                     .attr('class', 'header fillL');
90015
90016                 var message = messagewrap
90017                     .append('h3')
90018                     .text(_t('inspector.choose'));
90019
90020                 messagewrap
90021                     .append('button')
90022                     .attr('class', 'preset-choose')
90023                     .on('click', function() { dispatch$1.call('cancel', this); })
90024                     .call(svgIcon((_mainLocalizer.textDirection() === 'rtl') ? '#iD-icon-backward' : '#iD-icon-forward'));
90025
90026                 function initialKeydown() {
90027                     // hack to let delete shortcut work when search is autofocused
90028                     if (search.property('value').length === 0 &&
90029                         (event.keyCode === utilKeybinding.keyCodes['⌫'] ||
90030                          event.keyCode === utilKeybinding.keyCodes['⌦'])) {
90031                         event.preventDefault();
90032                         event.stopPropagation();
90033                         operationDelete(context, _entityIDs)();
90034
90035                     // hack to let undo work when search is autofocused
90036                     } else if (search.property('value').length === 0 &&
90037                         (event.ctrlKey || event.metaKey) &&
90038                         event.keyCode === utilKeybinding.keyCodes.z) {
90039                         event.preventDefault();
90040                         event.stopPropagation();
90041                         context.undo();
90042                     } else if (!event.ctrlKey && !event.metaKey) {
90043                         // don't check for delete/undo hack on future keydown events
90044                         select(this).on('keydown', keydown);
90045                         keydown.call(this);
90046                     }
90047                 }
90048
90049                 function keydown() {
90050                     // down arrow
90051                     if (event.keyCode === utilKeybinding.keyCodes['↓'] &&
90052                         // if insertion point is at the end of the string
90053                         search.node().selectionStart === search.property('value').length) {
90054                         event.preventDefault();
90055                         event.stopPropagation();
90056                         // move focus to the first item in the preset list
90057                         var buttons = list.selectAll('.preset-list-button');
90058                         if (!buttons.empty()) { buttons.nodes()[0].focus(); }
90059                     }
90060                 }
90061
90062                 function keypress() {
90063                     // enter
90064                     var value = search.property('value');
90065                     if (event.keyCode === 13 && value.length) {
90066                         list.selectAll('.preset-list-item:first-child')
90067                             .each(function(d) { d.choose.call(this); });
90068                     }
90069                 }
90070
90071                 function inputevent() {
90072                     var value = search.property('value');
90073                     list.classed('filtered', value.length);
90074                     var extent = combinedEntityExtent();
90075                     var results, messageText;
90076                     if (value.length && extent) {
90077                         var center = extent.center();
90078                         var countryCode = iso1A2Code(center);
90079
90080                         results = presets.search(value, entityGeometries()[0], countryCode && countryCode.toLowerCase());
90081                         messageText = _t('inspector.results', {
90082                             n: results.collection.length,
90083                             search: value
90084                         });
90085                     } else {
90086                         results = _mainPresetIndex.defaults(entityGeometries()[0], 36, !context.inIntro());
90087                         messageText = _t('inspector.choose');
90088                     }
90089                     list.call(drawList, results);
90090                     message.text(messageText);
90091                 }
90092
90093                 var searchWrap = selection
90094                     .append('div')
90095                     .attr('class', 'search-header');
90096
90097                 var search = searchWrap
90098                     .append('input')
90099                     .attr('class', 'preset-search-input')
90100                     .attr('placeholder', _t('inspector.search'))
90101                     .attr('type', 'search')
90102                     .call(utilNoAuto)
90103                     .on('keydown', initialKeydown)
90104                     .on('keypress', keypress)
90105                     .on('input', inputevent);
90106
90107                 searchWrap
90108                     .call(svgIcon('#iD-icon-search', 'pre-text'));
90109
90110                 if (_autofocus) {
90111                     search.node().focus();
90112                 }
90113
90114                 var listWrap = selection
90115                     .append('div')
90116                     .attr('class', 'inspector-body');
90117
90118                 var list = listWrap
90119                     .append('div')
90120                     .attr('class', 'preset-list')
90121                     .call(drawList, _mainPresetIndex.defaults(entityGeometries()[0], 36, !context.inIntro()));
90122
90123                 context.features().on('change.preset-list', updateForFeatureHiddenState);
90124             }
90125
90126
90127             function drawList(list, presets) {
90128                 presets = presets.matchAllGeometry(entityGeometries());
90129                 var collection = presets.collection.reduce(function(collection, preset) {
90130                     if (!preset) { return collection; }
90131
90132                     if (preset.members) {
90133                         if (preset.members.collection.filter(function(preset) {
90134                             return preset.addable();
90135                         }).length > 1) {
90136                             collection.push(CategoryItem(preset));
90137                         }
90138                     } else if (preset.addable()) {
90139                         collection.push(PresetItem(preset));
90140                     }
90141                     return collection;
90142                 }, []);
90143
90144                 var items = list.selectAll('.preset-list-item')
90145                     .data(collection, function(d) { return d.preset.id; });
90146
90147                 items.order();
90148
90149                 items.exit()
90150                     .remove();
90151
90152                 items.enter()
90153                     .append('div')
90154                     .attr('class', function(item) { return 'preset-list-item preset-' + item.preset.id.replace('/', '-'); })
90155                     .classed('current', function(item) { return _currentPresets.indexOf(item.preset) !== -1; })
90156                     .each(function(item) { select(this).call(item); })
90157                     .style('opacity', 0)
90158                     .transition()
90159                     .style('opacity', 1);
90160
90161                 updateForFeatureHiddenState();
90162             }
90163
90164             function itemKeydown(){
90165                 // the actively focused item
90166                 var item = select(this.closest('.preset-list-item'));
90167                 var parentItem = select(item.node().parentNode.closest('.preset-list-item'));
90168
90169                 // arrow down, move focus to the next, lower item
90170                 if (event.keyCode === utilKeybinding.keyCodes['↓']) {
90171                     event.preventDefault();
90172                     event.stopPropagation();
90173                     // the next item in the list at the same level
90174                     var nextItem = select(item.node().nextElementSibling);
90175                     // if there is no next item in this list
90176                     if (nextItem.empty()) {
90177                         // if there is a parent item
90178                         if (!parentItem.empty()) {
90179                             // the item is the last item of a sublist,
90180                             // select the next item at the parent level
90181                             nextItem = select(parentItem.node().nextElementSibling);
90182                         }
90183                     // if the focused item is expanded
90184                     } else if (select(this).classed('expanded')) {
90185                         // select the first subitem instead
90186                         nextItem = item.select('.subgrid .preset-list-item:first-child');
90187                     }
90188                     if (!nextItem.empty()) {
90189                         // focus on the next item
90190                         nextItem.select('.preset-list-button').node().focus();
90191                     }
90192
90193                 // arrow up, move focus to the previous, higher item
90194                 } else if (event.keyCode === utilKeybinding.keyCodes['↑']) {
90195                     event.preventDefault();
90196                     event.stopPropagation();
90197                     // the previous item in the list at the same level
90198                     var previousItem = select(item.node().previousElementSibling);
90199
90200                     // if there is no previous item in this list
90201                     if (previousItem.empty()) {
90202                         // if there is a parent item
90203                         if (!parentItem.empty()) {
90204                             // the item is the first subitem of a sublist select the parent item
90205                             previousItem = parentItem;
90206                         }
90207                     // if the previous item is expanded
90208                     } else if (previousItem.select('.preset-list-button').classed('expanded')) {
90209                         // select the last subitem of the sublist of the previous item
90210                         previousItem = previousItem.select('.subgrid .preset-list-item:last-child');
90211                     }
90212
90213                     if (!previousItem.empty()) {
90214                         // focus on the previous item
90215                         previousItem.select('.preset-list-button').node().focus();
90216                     } else {
90217                         // the focus is at the top of the list, move focus back to the search field
90218                         var search = select(this.closest('.preset-list-pane')).select('.preset-search-input');
90219                         search.node().focus();
90220                     }
90221
90222                 // arrow left, move focus to the parent item if there is one
90223                 } else if (event.keyCode === utilKeybinding.keyCodes[(_mainLocalizer.textDirection() === 'rtl') ? '→' : '←']) {
90224                     event.preventDefault();
90225                     event.stopPropagation();
90226                     // if there is a parent item, focus on the parent item
90227                     if (!parentItem.empty()) {
90228                         parentItem.select('.preset-list-button').node().focus();
90229                     }
90230
90231                 // arrow right, choose this item
90232                 } else if (event.keyCode === utilKeybinding.keyCodes[(_mainLocalizer.textDirection() === 'rtl') ? '←' : '→']) {
90233                     event.preventDefault();
90234                     event.stopPropagation();
90235                     item.datum().choose.call(select(this).node());
90236                 }
90237             }
90238
90239
90240             function CategoryItem(preset) {
90241                 var box, sublist, shown = false;
90242
90243                 function item(selection) {
90244                     var wrap = selection.append('div')
90245                         .attr('class', 'preset-list-button-wrap category');
90246
90247                     function click() {
90248                         var isExpanded = select(this).classed('expanded');
90249                         var iconName = isExpanded ?
90250                             (_mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-backward' : '#iD-icon-forward') : '#iD-icon-down';
90251                         select(this)
90252                             .classed('expanded', !isExpanded);
90253                         select(this).selectAll('div.label-inner svg.icon use')
90254                             .attr('href', iconName);
90255                         item.choose();
90256                     }
90257
90258                     var geometries = entityGeometries();
90259
90260                     var button = wrap
90261                         .append('button')
90262                         .attr('class', 'preset-list-button')
90263                         .classed('expanded', false)
90264                         .call(uiPresetIcon()
90265                             .geometry(geometries.length === 1 && geometries[0])
90266                             .preset(preset))
90267                         .on('click', click)
90268                         .on('keydown', function() {
90269                             // right arrow, expand the focused item
90270                             if (event.keyCode === utilKeybinding.keyCodes[(_mainLocalizer.textDirection() === 'rtl') ? '←' : '→']) {
90271                                 event.preventDefault();
90272                                 event.stopPropagation();
90273                                 // if the item isn't expanded
90274                                 if (!select(this).classed('expanded')) {
90275                                     // toggle expansion (expand the item)
90276                                     click.call(this);
90277                                 }
90278                             // left arrow, collapse the focused item
90279                             } else if (event.keyCode === utilKeybinding.keyCodes[(_mainLocalizer.textDirection() === 'rtl') ? '→' : '←']) {
90280                                 event.preventDefault();
90281                                 event.stopPropagation();
90282                                 // if the item is expanded
90283                                 if (select(this).classed('expanded')) {
90284                                     // toggle expansion (collapse the item)
90285                                     click.call(this);
90286                                 }
90287                             } else {
90288                                 itemKeydown.call(this);
90289                             }
90290                         });
90291
90292                     var label = button
90293                         .append('div')
90294                         .attr('class', 'label')
90295                         .append('div')
90296                         .attr('class', 'label-inner');
90297
90298                     label
90299                         .append('div')
90300                         .attr('class', 'namepart')
90301                         .call(svgIcon((_mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-backward' : '#iD-icon-forward'), 'inline'))
90302                         .append('span')
90303                         .html(function() { return preset.name() + '&hellip;'; });
90304
90305                     box = selection.append('div')
90306                         .attr('class', 'subgrid')
90307                         .style('max-height', '0px')
90308                         .style('opacity', 0);
90309
90310                     box.append('div')
90311                         .attr('class', 'arrow');
90312
90313                     sublist = box.append('div')
90314                         .attr('class', 'preset-list fillL3');
90315                 }
90316
90317
90318                 item.choose = function() {
90319                     if (!box || !sublist) { return; }
90320
90321                     if (shown) {
90322                         shown = false;
90323                         box.transition()
90324                             .duration(200)
90325                             .style('opacity', '0')
90326                             .style('max-height', '0px')
90327                             .style('padding-bottom', '0px');
90328                     } else {
90329                         shown = true;
90330                         var members = preset.members.matchAllGeometry(entityGeometries());
90331                         sublist.call(drawList, members);
90332                         box.transition()
90333                             .duration(200)
90334                             .style('opacity', '1')
90335                             .style('max-height', 200 + members.collection.length * 190 + 'px')
90336                             .style('padding-bottom', '10px');
90337                     }
90338                 };
90339
90340                 item.preset = preset;
90341                 return item;
90342             }
90343
90344
90345             function PresetItem(preset) {
90346                 function item(selection) {
90347                     var wrap = selection.append('div')
90348                         .attr('class', 'preset-list-button-wrap');
90349
90350                     var geometries = entityGeometries();
90351
90352                     var button = wrap.append('button')
90353                         .attr('class', 'preset-list-button')
90354                         .call(uiPresetIcon()
90355                             .geometry(geometries.length === 1 && geometries[0])
90356                             .preset(preset))
90357                         .on('click', item.choose)
90358                         .on('keydown', itemKeydown);
90359
90360                     var label = button
90361                         .append('div')
90362                         .attr('class', 'label')
90363                         .append('div')
90364                         .attr('class', 'label-inner');
90365
90366                     // NOTE: split/join on en-dash, not a hyphen (to avoid conflict with fr - nl names in Brussels etc)
90367                     label.selectAll('.namepart')
90368                         .data(preset.name().split(' – '))
90369                         .enter()
90370                         .append('div')
90371                         .attr('class', 'namepart')
90372                         .text(function(d) { return d; });
90373
90374                     wrap.call(item.reference.button);
90375                     selection.call(item.reference.body);
90376                 }
90377
90378                 item.choose = function() {
90379                     if (select(this).classed('disabled')) { return; }
90380                     if (!context.inIntro()) {
90381                         _mainPresetIndex.setMostRecent(preset, entityGeometries()[0]);
90382                     }
90383                     context.perform(
90384                         function(graph) {
90385                             for (var i in _entityIDs) {
90386                                 var entityID = _entityIDs[i];
90387                                 var oldPreset = _mainPresetIndex.match(graph.entity(entityID), graph);
90388                                 graph = actionChangePreset(entityID, oldPreset, preset)(graph);
90389                             }
90390                             return graph;
90391                         },
90392                         _t('operations.change_tags.annotation')
90393                     );
90394
90395                     context.validator().validate();  // rerun validation
90396                     dispatch$1.call('choose', this, preset);
90397                 };
90398
90399                 item.help = function() {
90400                     event.stopPropagation();
90401                     item.reference.toggle();
90402                 };
90403
90404                 item.preset = preset;
90405                 item.reference = uiTagReference(preset.reference());
90406
90407                 return item;
90408             }
90409
90410
90411             function updateForFeatureHiddenState() {
90412                 if (!_entityIDs.every(context.hasEntity)) { return; }
90413
90414                 var geometries = entityGeometries();
90415                 var button = context.container().selectAll('.preset-list .preset-list-button');
90416
90417                 // remove existing tooltips
90418                 button.call(uiTooltip().destroyAny);
90419
90420                 button.each(function(item, index) {
90421                     var hiddenPresetFeaturesId;
90422                     for (var i in geometries) {
90423                         hiddenPresetFeaturesId = context.features().isHiddenPreset(item.preset, geometries[i]);
90424                         if (hiddenPresetFeaturesId) { break; }
90425                     }
90426                     var isHiddenPreset = !context.inIntro() &&
90427                         !!hiddenPresetFeaturesId &&
90428                         (_currentPresets.length !== 1 || item.preset !== _currentPresets[0]);
90429
90430                     select(this)
90431                         .classed('disabled', isHiddenPreset);
90432
90433                     if (isHiddenPreset) {
90434                         var isAutoHidden = context.features().autoHidden(hiddenPresetFeaturesId);
90435                         var tooltipIdSuffix = isAutoHidden ? 'zoom' : 'manual';
90436                         var tooltipObj = { features: _t('feature.' + hiddenPresetFeaturesId + '.description') };
90437                         select(this).call(uiTooltip()
90438                             .title(_t('inspector.hidden_preset.' + tooltipIdSuffix, tooltipObj))
90439                             .placement(index < 2 ? 'bottom' : 'top')
90440                         );
90441                     }
90442                 });
90443             }
90444
90445             presetList.autofocus = function(val) {
90446                 if (!arguments.length) { return _autofocus; }
90447                 _autofocus = val;
90448                 return presetList;
90449             };
90450
90451             presetList.entityIDs = function(val) {
90452                 if (!arguments.length) { return _entityIDs; }
90453                 _entityIDs = val;
90454                 if (_entityIDs && _entityIDs.length) {
90455                     var presets = _entityIDs.map(function(entityID) {
90456                         return _mainPresetIndex.match(context.entity(entityID), context.graph());
90457                     });
90458                     presetList.presets(presets);
90459                 }
90460                 return presetList;
90461             };
90462
90463             presetList.presets = function(val) {
90464                 if (!arguments.length) { return _currentPresets; }
90465                 _currentPresets = val;
90466                 return presetList;
90467             };
90468
90469             function entityGeometries() {
90470
90471                 var counts = {};
90472
90473                 for (var i in _entityIDs) {
90474                     var entityID = _entityIDs[i];
90475                     var entity = context.entity(entityID);
90476                     var geometry = entity.geometry(context.graph());
90477
90478                     // Treat entities on addr:interpolation lines as points, not vertices (#3241)
90479                     if (geometry === 'vertex' && entity.isOnAddressLine(context.graph())) {
90480                         geometry = 'point';
90481                     }
90482
90483                     if (!counts[geometry]) { counts[geometry] = 0; }
90484                     counts[geometry] += 1;
90485                 }
90486
90487                 return Object.keys(counts).sort(function(geom1, geom2) {
90488                     return counts[geom2] - counts[geom1];
90489                 });
90490             }
90491
90492             function combinedEntityExtent() {
90493                 return _entityIDs.reduce(function(extent, entityID) {
90494                     var entity = context.graph().entity(entityID);
90495                     return extent.extend(entity.extent(context.graph()));
90496                 }, geoExtent());
90497             }
90498
90499             return utilRebind(presetList, dispatch$1, 'on');
90500         }
90501
90502         function uiInspector(context) {
90503             var presetList = uiPresetList(context);
90504             var entityEditor = uiEntityEditor(context);
90505             var wrap = select(null),
90506                 presetPane = select(null),
90507                 editorPane = select(null);
90508             var _state = 'select';
90509             var _entityIDs;
90510             var _newFeature = false;
90511
90512
90513             function inspector(selection) {
90514                 presetList
90515                     .entityIDs(_entityIDs)
90516                     .autofocus(_newFeature)
90517                     .on('choose', inspector.setPreset)
90518                     .on('cancel', function() {
90519                         wrap.transition()
90520                             .styleTween('right', function() { return interpolate('-100%', '0%'); });
90521                         editorPane.call(entityEditor);
90522                     });
90523
90524                 entityEditor
90525                     .state(_state)
90526                     .entityIDs(_entityIDs)
90527                     .on('choose', inspector.showList);
90528
90529                 wrap = selection.selectAll('.panewrap')
90530                     .data([0]);
90531
90532                 var enter = wrap.enter()
90533                     .append('div')
90534                     .attr('class', 'panewrap');
90535
90536                 enter
90537                     .append('div')
90538                     .attr('class', 'preset-list-pane pane');
90539
90540                 enter
90541                     .append('div')
90542                     .attr('class', 'entity-editor-pane pane');
90543
90544                 wrap = wrap.merge(enter);
90545                 presetPane = wrap.selectAll('.preset-list-pane');
90546                 editorPane = wrap.selectAll('.entity-editor-pane');
90547
90548                 function shouldDefaultToPresetList() {
90549                     // always show the inspector on hover
90550                     if (_state !== 'select') { return false; }
90551
90552                     // can only change preset on single selection
90553                     if (_entityIDs.length !== 1) { return false; }
90554
90555                     var entityID = _entityIDs[0];
90556                     var entity = context.hasEntity(entityID);
90557                     if (!entity) { return false; }
90558
90559                     // default to inspector if there are already tags
90560                     if (entity.hasNonGeometryTags()) { return false; }
90561
90562                     // prompt to select preset if feature is new and untagged
90563                     if (_newFeature) { return true; }
90564
90565                     // all existing features except vertices should default to inspector
90566                     if (entity.geometry(context.graph()) !== 'vertex') { return false; }
90567
90568                     // show vertex relations if any
90569                     if (context.graph().parentRelations(entity).length) { return false; }
90570
90571                     // show vertex issues if there are any
90572                     if (context.validator().getEntityIssues(entityID).length) { return false; }
90573
90574                     // show turn retriction editor for junction vertices
90575                     if (entity.isHighwayIntersection(context.graph())) { return false; }
90576
90577                     // otherwise show preset list for uninteresting vertices
90578                     return true;
90579                 }
90580
90581                 if (shouldDefaultToPresetList()) {
90582                     wrap.style('right', '-100%');
90583                     presetPane.call(presetList);
90584                 } else {
90585                     wrap.style('right', '0%');
90586                     editorPane.call(entityEditor);
90587                 }
90588
90589                 var footer = selection.selectAll('.footer')
90590                     .data([0]);
90591
90592                 footer = footer.enter()
90593                     .append('div')
90594                     .attr('class', 'footer')
90595                     .merge(footer);
90596
90597                 footer
90598                     .call(uiViewOnOSM(context)
90599                         .what(context.hasEntity(_entityIDs.length === 1 && _entityIDs[0]))
90600                     );
90601             }
90602
90603             inspector.showList = function(presets) {
90604
90605                 wrap.transition()
90606                     .styleTween('right', function() { return interpolate('0%', '-100%'); });
90607
90608                 if (presets) {
90609                     presetList.presets(presets);
90610                 }
90611
90612                 presetPane
90613                     .call(presetList.autofocus(true));
90614             };
90615
90616             inspector.setPreset = function(preset) {
90617
90618                 // upon setting multipolygon, go to the area preset list instead of the editor
90619                 if (preset.id === 'type/multipolygon') {
90620                     presetPane
90621                         .call(presetList.autofocus(true));
90622
90623                 } else {
90624                     wrap.transition()
90625                         .styleTween('right', function() { return interpolate('-100%', '0%'); });
90626
90627                     editorPane
90628                         .call(entityEditor.presets([preset]));
90629                 }
90630
90631             };
90632
90633             inspector.state = function(val) {
90634                 if (!arguments.length) { return _state; }
90635                 _state = val;
90636                 entityEditor.state(_state);
90637
90638                 // remove any old field help overlay that might have gotten attached to the inspector
90639                 context.container().selectAll('.field-help-body').remove();
90640
90641                 return inspector;
90642             };
90643
90644
90645             inspector.entityIDs = function(val) {
90646                 if (!arguments.length) { return _entityIDs; }
90647                 _entityIDs = val;
90648                 return inspector;
90649             };
90650
90651
90652             inspector.newFeature = function(val) {
90653                 if (!arguments.length) { return _newFeature; }
90654                 _newFeature = val;
90655                 return inspector;
90656             };
90657
90658
90659             return inspector;
90660         }
90661
90662         function uiSidebar(context) {
90663             var inspector = uiInspector(context);
90664             var dataEditor = uiDataEditor(context);
90665             var noteEditor = uiNoteEditor(context);
90666             var improveOsmEditor = uiImproveOsmEditor(context);
90667             var keepRightEditor = uiKeepRightEditor(context);
90668             var osmoseEditor = uiOsmoseEditor(context);
90669             var _current;
90670             var _wasData = false;
90671             var _wasNote = false;
90672             var _wasQaItem = false;
90673
90674             // use pointer events on supported platforms; fallback to mouse events
90675             var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
90676
90677
90678             function sidebar(selection) {
90679                 var container = context.container();
90680                 var minWidth = 240;
90681                 var sidebarWidth;
90682                 var containerWidth;
90683                 var dragOffset;
90684
90685                 // Set the initial width constraints
90686                 selection
90687                     .style('min-width', minWidth + 'px')
90688                     .style('max-width', '400px')
90689                     .style('width', '33.3333%');
90690
90691                 var resizer = selection
90692                     .append('div')
90693                     .attr('class', 'sidebar-resizer')
90694                     .on(_pointerPrefix + 'down.sidebar-resizer', pointerdown);
90695
90696                 var downPointerId, lastClientX, containerLocGetter;
90697
90698                 function pointerdown() {
90699                     if (downPointerId) { return; }
90700
90701                     if ('button' in event && event.button !== 0) { return; }
90702
90703                     downPointerId = event.pointerId || 'mouse';
90704
90705                     lastClientX = event.clientX;
90706
90707                     containerLocGetter = utilFastMouse(container.node());
90708
90709                     // offset from edge of sidebar-resizer
90710                     dragOffset = utilFastMouse(resizer.node())(event)[0] - 1;
90711
90712                     sidebarWidth = selection.node().getBoundingClientRect().width;
90713                     containerWidth = container.node().getBoundingClientRect().width;
90714                     var widthPct = (sidebarWidth / containerWidth) * 100;
90715                     selection
90716                         .style('width', widthPct + '%')    // lock in current width
90717                         .style('max-width', '85%');        // but allow larger widths
90718
90719                     resizer.classed('dragging', true);
90720
90721                     select(window)
90722                         .on('touchmove.sidebar-resizer', function() {
90723                             // disable page scrolling while resizing on touch input
90724                             event.preventDefault();
90725                         }, { passive: false })
90726                         .on(_pointerPrefix + 'move.sidebar-resizer', pointermove)
90727                         .on(_pointerPrefix + 'up.sidebar-resizer pointercancel.sidebar-resizer', pointerup);
90728                 }
90729
90730                 function pointermove() {
90731
90732                     if (downPointerId !== (event.pointerId || 'mouse')) { return; }
90733
90734                     event.preventDefault();
90735
90736                     var dx = event.clientX - lastClientX;
90737
90738                     lastClientX = event.clientX;
90739
90740                     var isRTL = (_mainLocalizer.textDirection() === 'rtl');
90741                     var scaleX = isRTL ? 0 : 1;
90742                     var xMarginProperty = isRTL ? 'margin-right' : 'margin-left';
90743
90744                     var x = containerLocGetter(event)[0] - dragOffset;
90745                     sidebarWidth = isRTL ? containerWidth - x : x;
90746
90747                     var isCollapsed = selection.classed('collapsed');
90748                     var shouldCollapse = sidebarWidth < minWidth;
90749
90750                     selection.classed('collapsed', shouldCollapse);
90751
90752                     if (shouldCollapse) {
90753                         if (!isCollapsed) {
90754                             selection
90755                                 .style(xMarginProperty, '-400px')
90756                                 .style('width', '400px');
90757
90758                             context.ui().onResize([(sidebarWidth - dx) * scaleX, 0]);
90759                         }
90760
90761                     } else {
90762                         var widthPct = (sidebarWidth / containerWidth) * 100;
90763                         selection
90764                             .style(xMarginProperty, null)
90765                             .style('width', widthPct + '%');
90766
90767                         if (isCollapsed) {
90768                             context.ui().onResize([-sidebarWidth * scaleX, 0]);
90769                         } else {
90770                             context.ui().onResize([-dx * scaleX, 0]);
90771                         }
90772                     }
90773                 }
90774
90775                 function pointerup() {
90776                     if (downPointerId !== (event.pointerId || 'mouse')) { return; }
90777
90778                     downPointerId = null;
90779
90780                     resizer.classed('dragging', false);
90781
90782                     select(window)
90783                         .on('touchmove.sidebar-resizer', null)
90784                         .on(_pointerPrefix + 'move.sidebar-resizer', null)
90785                         .on(_pointerPrefix + 'up.sidebar-resizer pointercancel.sidebar-resizer', null);
90786                 }
90787
90788                 var featureListWrap = selection
90789                     .append('div')
90790                     .attr('class', 'feature-list-pane')
90791                     .call(uiFeatureList(context));
90792
90793                 var inspectorWrap = selection
90794                     .append('div')
90795                     .attr('class', 'inspector-hidden inspector-wrap');
90796
90797                 var hoverModeSelect = function(targets) {
90798                     context.container().selectAll('.feature-list-item').classed('hover', false);
90799
90800                     if (context.selectedIDs().length > 1 &&
90801                         targets && targets.length) {
90802
90803                         var elements = context.container().selectAll('.feature-list-item')
90804                             .filter(function (node) {
90805                                 return targets.indexOf(node) !== -1;
90806                             });
90807
90808                         if (!elements.empty()) {
90809                             elements.classed('hover', true);
90810                         }
90811                     }
90812                 };
90813
90814                 sidebar.hoverModeSelect = throttle(hoverModeSelect, 200);
90815
90816                 function hover(targets) {
90817                     var datum = targets && targets.length && targets[0];
90818                     if (datum && datum.__featurehash__) {   // hovering on data
90819                         _wasData = true;
90820                         sidebar
90821                             .show(dataEditor.datum(datum));
90822
90823                         selection.selectAll('.sidebar-component')
90824                             .classed('inspector-hover', true);
90825
90826                     } else if (datum instanceof osmNote) {
90827                         if (context.mode().id === 'drag-note') { return; }
90828                         _wasNote = true;
90829
90830                         var osm = services.osm;
90831                         if (osm) {
90832                             datum = osm.getNote(datum.id);   // marker may contain stale data - get latest
90833                         }
90834
90835                         sidebar
90836                             .show(noteEditor.note(datum));
90837
90838                         selection.selectAll('.sidebar-component')
90839                             .classed('inspector-hover', true);
90840
90841                     } else if (datum instanceof QAItem) {
90842                         _wasQaItem = true;
90843
90844                         var errService = services[datum.service];
90845                         if (errService) {
90846                             // marker may contain stale data - get latest
90847                             datum = errService.getError(datum.id);
90848                         }
90849
90850                         // Currently only three possible services
90851                         var errEditor;
90852                         if (datum.service === 'keepRight') {
90853                             errEditor = keepRightEditor;
90854                         } else if (datum.service === 'osmose') {
90855                             errEditor = osmoseEditor;
90856                         } else {
90857                             errEditor = improveOsmEditor;
90858                         }
90859
90860                         context.container().selectAll('.qaItem.' + datum.service)
90861                             .classed('hover', function(d) { return d.id === datum.id; });
90862
90863                         sidebar
90864                             .show(errEditor.error(datum));
90865
90866                         selection.selectAll('.sidebar-component')
90867                             .classed('inspector-hover', true);
90868
90869                     } else if (!_current && (datum instanceof osmEntity)) {
90870                         featureListWrap
90871                             .classed('inspector-hidden', true);
90872
90873                         inspectorWrap
90874                             .classed('inspector-hidden', false)
90875                             .classed('inspector-hover', true);
90876
90877                         if (!inspector.entityIDs() || !utilArrayIdentical(inspector.entityIDs(), [datum.id]) || inspector.state() !== 'hover') {
90878                             inspector
90879                                 .state('hover')
90880                                 .entityIDs([datum.id])
90881                                 .newFeature(false);
90882
90883                             inspectorWrap
90884                                 .call(inspector);
90885                         }
90886
90887                     } else if (!_current) {
90888                         featureListWrap
90889                             .classed('inspector-hidden', false);
90890                         inspectorWrap
90891                             .classed('inspector-hidden', true);
90892                         inspector
90893                             .state('hide');
90894
90895                     } else if (_wasData || _wasNote || _wasQaItem) {
90896                         _wasNote = false;
90897                         _wasData = false;
90898                         _wasQaItem = false;
90899                         context.container().selectAll('.note').classed('hover', false);
90900                         context.container().selectAll('.qaItem').classed('hover', false);
90901                         sidebar.hide();
90902                     }
90903                 }
90904
90905                 sidebar.hover = throttle(hover, 200);
90906
90907
90908                 sidebar.intersects = function(extent) {
90909                     var rect = selection.node().getBoundingClientRect();
90910                     return extent.intersects([
90911                         context.projection.invert([0, rect.height]),
90912                         context.projection.invert([rect.width, 0])
90913                     ]);
90914                 };
90915
90916
90917                 sidebar.select = function(ids, newFeature) {
90918                     sidebar.hide();
90919
90920                     if (ids && ids.length) {
90921
90922                         var entity = ids.length === 1 && context.entity(ids[0]);
90923                         if (entity && newFeature && selection.classed('collapsed')) {
90924                             // uncollapse the sidebar
90925                             var extent = entity.extent(context.graph());
90926                             sidebar.expand(sidebar.intersects(extent));
90927                         }
90928
90929                         featureListWrap
90930                             .classed('inspector-hidden', true);
90931
90932                         inspectorWrap
90933                             .classed('inspector-hidden', false)
90934                             .classed('inspector-hover', false);
90935
90936                         // reload the UI even if the ids are the same since the entities
90937                         // themselves may have changed
90938                         inspector
90939                             .state('select')
90940                             .entityIDs(ids)
90941                             .newFeature(newFeature);
90942
90943                         inspectorWrap
90944                             .call(inspector);
90945
90946                     } else {
90947                         inspector
90948                             .state('hide');
90949                     }
90950                 };
90951
90952
90953                 sidebar.showPresetList = function() {
90954                     inspector.showList();
90955                 };
90956
90957
90958                 sidebar.show = function(component, element) {
90959                     featureListWrap
90960                         .classed('inspector-hidden', true);
90961                     inspectorWrap
90962                         .classed('inspector-hidden', true);
90963
90964                     if (_current) { _current.remove(); }
90965                     _current = selection
90966                         .append('div')
90967                         .attr('class', 'sidebar-component')
90968                         .call(component, element);
90969                 };
90970
90971
90972                 sidebar.hide = function() {
90973                     featureListWrap
90974                         .classed('inspector-hidden', false);
90975                     inspectorWrap
90976                         .classed('inspector-hidden', true);
90977
90978                     if (_current) { _current.remove(); }
90979                     _current = null;
90980                 };
90981
90982
90983                 sidebar.expand = function(moveMap) {
90984                     if (selection.classed('collapsed')) {
90985                         sidebar.toggle(moveMap);
90986                     }
90987                 };
90988
90989
90990                 sidebar.collapse = function(moveMap) {
90991                     if (!selection.classed('collapsed')) {
90992                         sidebar.toggle(moveMap);
90993                     }
90994                 };
90995
90996
90997                 sidebar.toggle = function(moveMap) {
90998                     var e = event;
90999                     if (e && e.sourceEvent) {
91000                         e.sourceEvent.preventDefault();
91001                     } else if (e) {
91002                         e.preventDefault();
91003                     }
91004
91005                     // Don't allow sidebar to toggle when the user is in the walkthrough.
91006                     if (context.inIntro()) { return; }
91007
91008                     var isCollapsed = selection.classed('collapsed');
91009                     var isCollapsing = !isCollapsed;
91010                     var isRTL = (_mainLocalizer.textDirection() === 'rtl');
91011                     var scaleX = isRTL ? 0 : 1;
91012                     var xMarginProperty = isRTL ? 'margin-right' : 'margin-left';
91013
91014                     sidebarWidth = selection.node().getBoundingClientRect().width;
91015
91016                     // switch from % to px
91017                     selection.style('width', sidebarWidth + 'px');
91018
91019                     var startMargin, endMargin, lastMargin;
91020                     if (isCollapsing) {
91021                         startMargin = lastMargin = 0;
91022                         endMargin = -sidebarWidth;
91023                     } else {
91024                         startMargin = lastMargin = -sidebarWidth;
91025                         endMargin = 0;
91026                     }
91027
91028                     selection.transition()
91029                         .style(xMarginProperty, endMargin + 'px')
91030                         .tween('panner', function() {
91031                             var i = d3_interpolateNumber(startMargin, endMargin);
91032                             return function(t) {
91033                                 var dx = lastMargin - Math.round(i(t));
91034                                 lastMargin = lastMargin - dx;
91035                                 context.ui().onResize(moveMap ? undefined : [dx * scaleX, 0]);
91036                             };
91037                         })
91038                         .on('end', function() {
91039                             selection.classed('collapsed', isCollapsing);
91040
91041                             // switch back from px to %
91042                             if (!isCollapsing) {
91043                                 var containerWidth = container.node().getBoundingClientRect().width;
91044                                 var widthPct = (sidebarWidth / containerWidth) * 100;
91045                                 selection
91046                                     .style(xMarginProperty, null)
91047                                     .style('width', widthPct + '%');
91048                             }
91049                         });
91050                 };
91051
91052                 // toggle the sidebar collapse when double-clicking the resizer
91053                 resizer.on('dblclick', sidebar.toggle);
91054
91055                 // ensure hover sidebar is closed when zooming out beyond editable zoom
91056                 context.map().on('crossEditableZoom.sidebar', function(within) {
91057                     if (!within && !selection.select('.inspector-hover').empty()) {
91058                         hover([]);
91059                     }
91060                 });
91061             }
91062
91063             sidebar.showPresetList = function() {};
91064             sidebar.hover = function() {};
91065             sidebar.hover.cancel = function() {};
91066             sidebar.intersects = function() {};
91067             sidebar.select = function() {};
91068             sidebar.show = function() {};
91069             sidebar.hide = function() {};
91070             sidebar.expand = function() {};
91071             sidebar.collapse = function() {};
91072             sidebar.toggle = function() {};
91073
91074             return sidebar;
91075         }
91076
91077         function uiSourceSwitch(context) {
91078             var keys;
91079
91080
91081             function click() {
91082                 event.preventDefault();
91083
91084                 var osm = context.connection();
91085                 if (!osm) { return; }
91086
91087                 if (context.inIntro()) { return; }
91088
91089                 if (context.history().hasChanges() &&
91090                     !window.confirm(_t('source_switch.lose_changes'))) { return; }
91091
91092                 var isLive = select(this)
91093                     .classed('live');
91094
91095                 isLive = !isLive;
91096                 context.enter(modeBrowse(context));
91097                 context.history().clearSaved();          // remove saved history
91098                 context.flush();                         // remove stored data
91099
91100                 select(this)
91101                     .text(isLive ? _t('source_switch.live') : _t('source_switch.dev'))
91102                     .classed('live', isLive)
91103                     .classed('chip', isLive);
91104
91105                 osm.switch(isLive ? keys[0] : keys[1]);  // switch connection (warning: dispatches 'change' event)
91106             }
91107
91108             var sourceSwitch = function(selection) {
91109                 selection
91110                     .append('a')
91111                     .attr('href', '#')
91112                     .text(_t('source_switch.live'))
91113                     .attr('class', 'live chip')
91114                     .on('click', click);
91115             };
91116
91117
91118             sourceSwitch.keys = function(_) {
91119                 if (!arguments.length) { return keys; }
91120                 keys = _;
91121                 return sourceSwitch;
91122             };
91123
91124
91125             return sourceSwitch;
91126         }
91127
91128         function uiSpinner(context) {
91129             var osm = context.connection();
91130
91131
91132             return function(selection) {
91133                 var img = selection
91134                     .append('img')
91135                     .attr('src', context.imagePath('loader-black.gif'))
91136                     .style('opacity', 0);
91137
91138                 if (osm) {
91139                     osm
91140                         .on('loading.spinner', function() {
91141                             img.transition()
91142                                 .style('opacity', 1);
91143                         })
91144                         .on('loaded.spinner', function() {
91145                             img.transition()
91146                                 .style('opacity', 0);
91147                         });
91148                 }
91149             };
91150         }
91151
91152         function uiSplash(context) {
91153           return function (selection) {
91154             // Exception - if there are restorable changes, skip this splash screen.
91155             // This is because we currently only support one `uiModal` at a time
91156             //  and we need to show them `uiRestore`` instead of this one.
91157             if (context.history().hasRestorableChanges()) { return; }
91158
91159             // If user has not seen this version of the privacy policy, show the splash again.
91160             var updateMessage = '';
91161             var sawPrivacyVersion = corePreferences('sawPrivacyVersion');
91162             var showSplash = !corePreferences('sawSplash');
91163             if (sawPrivacyVersion !== context.privacyVersion) {
91164               updateMessage = _t('splash.privacy_update');
91165               showSplash = true;
91166             }
91167
91168             if (!showSplash) { return; }
91169
91170             corePreferences('sawSplash', true);
91171             corePreferences('sawPrivacyVersion', context.privacyVersion);
91172
91173             // fetch intro graph data now, while user is looking at the splash screen
91174             _mainFileFetcher.get('intro_graph');
91175
91176             var modalSelection = uiModal(selection);
91177
91178             modalSelection.select('.modal')
91179               .attr('class', 'modal-splash modal');
91180
91181             var introModal = modalSelection.select('.content')
91182               .append('div')
91183               .attr('class', 'fillL');
91184
91185             introModal
91186               .append('div')
91187               .attr('class','modal-section')
91188               .append('h3')
91189               .text(_t('splash.welcome'));
91190
91191             var modalSection = introModal
91192               .append('div')
91193               .attr('class','modal-section');
91194
91195             modalSection
91196               .append('p')
91197               .html(_t('splash.text', {
91198                 version: context.version,
91199                 website: '<a target="_blank" href="http://ideditor.blog/">ideditor.blog</a>',
91200                 github: '<a target="_blank" href="https://github.com/openstreetmap/iD">github.com</a>'
91201               }));
91202
91203             modalSection
91204               .append('p')
91205               .html(_t('splash.privacy', {
91206                 updateMessage: updateMessage,
91207                 privacyLink: '<a target="_blank" href="https://github.com/openstreetmap/iD/blob/release/PRIVACY.md">' +
91208                   _t('splash.privacy_policy') + '</a>'
91209               }));
91210
91211             var buttonWrap = introModal
91212               .append('div')
91213               .attr('class', 'modal-actions');
91214
91215             var walkthrough = buttonWrap
91216               .append('button')
91217               .attr('class', 'walkthrough')
91218               .on('click', function () {
91219                 context.container().call(uiIntro(context));
91220                 modalSelection.close();
91221               });
91222
91223             walkthrough
91224               .append('svg')
91225               .attr('class', 'logo logo-walkthrough')
91226               .append('use')
91227               .attr('xlink:href', '#iD-logo-walkthrough');
91228
91229             walkthrough
91230               .append('div')
91231               .text(_t('splash.walkthrough'));
91232
91233             var startEditing = buttonWrap
91234               .append('button')
91235               .attr('class', 'start-editing')
91236               .on('click', modalSelection.close);
91237
91238             startEditing
91239               .append('svg')
91240               .attr('class', 'logo logo-features')
91241               .append('use')
91242               .attr('xlink:href', '#iD-logo-features');
91243
91244             startEditing
91245               .append('div')
91246               .text(_t('splash.start'));
91247
91248             modalSelection.select('button.close')
91249               .attr('class','hide');
91250           };
91251         }
91252
91253         function uiStatus(context) {
91254             var osm = context.connection();
91255
91256
91257             return function(selection) {
91258                 if (!osm) { return; }
91259
91260                 function update(err, apiStatus) {
91261                     selection.html('');
91262
91263                     if (err) {
91264                         if (apiStatus === 'connectionSwitched') {
91265                             // if the connection was just switched, we can't rely on
91266                             // the status (we're getting the status of the previous api)
91267                             return;
91268
91269                         } else if (apiStatus === 'rateLimited') {
91270                             selection
91271                                 .text(_t('osm_api_status.message.rateLimit'))
91272                                 .append('a')
91273                                 .attr('class', 'api-status-login')
91274                                 .attr('target', '_blank')
91275                                 .call(svgIcon('#iD-icon-out-link', 'inline'))
91276                                 .append('span')
91277                                 .text(_t('login'))
91278                                 .on('click.login', function() {
91279                                     event.preventDefault();
91280                                     osm.authenticate();
91281                                 });
91282                         } else {
91283
91284                             // don't allow retrying too rapidly
91285                             var throttledRetry = throttle(function() {
91286                                 // try loading the visible tiles
91287                                 context.loadTiles(context.projection);
91288                                 // manually reload the status too in case all visible tiles were already loaded
91289                                 osm.reloadApiStatus();
91290                             }, 2000);
91291
91292                             // eslint-disable-next-line no-warning-comments
91293                             // TODO: nice messages for different error types
91294                             selection
91295                                 .text(_t('osm_api_status.message.error') + ' ')
91296                                 .append('a')
91297                                 // let the user manually retry their connection directly
91298                                 .text(_t('osm_api_status.retry'))
91299                                 .on('click.retry', function() {
91300                                     event.preventDefault();
91301                                     throttledRetry();
91302                                 });
91303                         }
91304
91305                     } else if (apiStatus === 'readonly') {
91306                         selection.text(_t('osm_api_status.message.readonly'));
91307                     } else if (apiStatus === 'offline') {
91308                         selection.text(_t('osm_api_status.message.offline'));
91309                     }
91310
91311                     selection.attr('class', 'api-status ' + (err ? 'error' : apiStatus));
91312                 }
91313
91314                 osm.on('apiStatusChange.uiStatus', update);
91315
91316                 // reload the status periodically regardless of other factors
91317                 window.setInterval(function() {
91318                     osm.reloadApiStatus();
91319                 }, 90000);
91320
91321                 // load the initial status in case no OSM data was loaded yet
91322                 osm.reloadApiStatus();
91323             };
91324         }
91325
91326         function modeDrawArea(context, wayID, startGraph, button) {
91327             var mode = {
91328                 button: button,
91329                 id: 'draw-area'
91330             };
91331
91332             var behavior = behaviorDrawWay(context, wayID, mode, startGraph)
91333                 .on('rejectedSelfIntersection.modeDrawArea', function() {
91334                     context.ui().flash
91335                         .text(_t('self_intersection.error.areas'))();
91336                 });
91337
91338             mode.wayID = wayID;
91339
91340             mode.enter = function() {
91341                 context.install(behavior);
91342             };
91343
91344             mode.exit = function() {
91345                 context.uninstall(behavior);
91346             };
91347
91348             mode.selectedIDs = function() {
91349                 return [wayID];
91350             };
91351
91352             mode.activeID = function() {
91353                 return (behavior && behavior.activeID()) || [];
91354             };
91355
91356             return mode;
91357         }
91358
91359         function modeAddArea(context, mode) {
91360             mode.id = 'add-area';
91361
91362             var behavior = behaviorAddWay(context)
91363                 .on('start', start)
91364                 .on('startFromWay', startFromWay)
91365                 .on('startFromNode', startFromNode);
91366
91367             var defaultTags = { area: 'yes' };
91368             if (mode.preset) { defaultTags = mode.preset.setTags(defaultTags, 'area'); }
91369
91370
91371             function actionClose(wayId) {
91372                 return function (graph) {
91373                     return graph.replace(graph.entity(wayId).close());
91374                 };
91375             }
91376
91377
91378             function start(loc) {
91379                 var startGraph = context.graph();
91380                 var node = osmNode({ loc: loc });
91381                 var way = osmWay({ tags: defaultTags });
91382
91383                 context.perform(
91384                     actionAddEntity(node),
91385                     actionAddEntity(way),
91386                     actionAddVertex(way.id, node.id),
91387                     actionClose(way.id)
91388                 );
91389
91390                 context.enter(modeDrawArea(context, way.id, startGraph, mode.button));
91391             }
91392
91393
91394             function startFromWay(loc, edge) {
91395                 var startGraph = context.graph();
91396                 var node = osmNode({ loc: loc });
91397                 var way = osmWay({ tags: defaultTags });
91398
91399                 context.perform(
91400                     actionAddEntity(node),
91401                     actionAddEntity(way),
91402                     actionAddVertex(way.id, node.id),
91403                     actionClose(way.id),
91404                     actionAddMidpoint({ loc: loc, edge: edge }, node)
91405                 );
91406
91407                 context.enter(modeDrawArea(context, way.id, startGraph, mode.button));
91408             }
91409
91410
91411             function startFromNode(node) {
91412                 var startGraph = context.graph();
91413                 var way = osmWay({ tags: defaultTags });
91414
91415                 context.perform(
91416                     actionAddEntity(way),
91417                     actionAddVertex(way.id, node.id),
91418                     actionClose(way.id)
91419                 );
91420
91421                 context.enter(modeDrawArea(context, way.id, startGraph, mode.button));
91422             }
91423
91424
91425             mode.enter = function() {
91426                 context.install(behavior);
91427             };
91428
91429
91430             mode.exit = function() {
91431                 context.uninstall(behavior);
91432             };
91433
91434
91435             return mode;
91436         }
91437
91438         function modeAddLine(context, mode) {
91439             mode.id = 'add-line';
91440
91441             var behavior = behaviorAddWay(context)
91442                 .on('start', start)
91443                 .on('startFromWay', startFromWay)
91444                 .on('startFromNode', startFromNode);
91445
91446             var defaultTags = {};
91447             if (mode.preset) { defaultTags = mode.preset.setTags(defaultTags, 'line'); }
91448
91449
91450             function start(loc) {
91451                 var startGraph = context.graph();
91452                 var node = osmNode({ loc: loc });
91453                 var way = osmWay({ tags: defaultTags });
91454
91455                 context.perform(
91456                     actionAddEntity(node),
91457                     actionAddEntity(way),
91458                     actionAddVertex(way.id, node.id)
91459                 );
91460
91461                 context.enter(modeDrawLine(context, way.id, startGraph, mode.button));
91462             }
91463
91464
91465             function startFromWay(loc, edge) {
91466                 var startGraph = context.graph();
91467                 var node = osmNode({ loc: loc });
91468                 var way = osmWay({ tags: defaultTags });
91469
91470                 context.perform(
91471                     actionAddEntity(node),
91472                     actionAddEntity(way),
91473                     actionAddVertex(way.id, node.id),
91474                     actionAddMidpoint({ loc: loc, edge: edge }, node)
91475                 );
91476
91477                 context.enter(modeDrawLine(context, way.id, startGraph, mode.button));
91478             }
91479
91480
91481             function startFromNode(node) {
91482                 var startGraph = context.graph();
91483                 var way = osmWay({ tags: defaultTags });
91484
91485                 context.perform(
91486                     actionAddEntity(way),
91487                     actionAddVertex(way.id, node.id)
91488                 );
91489
91490                 context.enter(modeDrawLine(context, way.id, startGraph, mode.button));
91491             }
91492
91493
91494             mode.enter = function() {
91495                 context.install(behavior);
91496             };
91497
91498
91499             mode.exit = function() {
91500                 context.uninstall(behavior);
91501             };
91502
91503             return mode;
91504         }
91505
91506         function modeAddPoint(context, mode) {
91507
91508             mode.id = 'add-point';
91509
91510             var behavior = behaviorDraw(context)
91511                 .on('click', add)
91512                 .on('clickWay', addWay)
91513                 .on('clickNode', addNode)
91514                 .on('cancel', cancel)
91515                 .on('finish', cancel);
91516
91517             var defaultTags = {};
91518             if (mode.preset) { defaultTags = mode.preset.setTags(defaultTags, 'point'); }
91519
91520
91521             function add(loc) {
91522                 var node = osmNode({ loc: loc, tags: defaultTags });
91523
91524                 context.perform(
91525                     actionAddEntity(node),
91526                     _t('operations.add.annotation.point')
91527                 );
91528
91529                 enterSelectMode(node);
91530             }
91531
91532
91533             function addWay(loc, edge) {
91534                 var node = osmNode({ tags: defaultTags });
91535
91536                 context.perform(
91537                     actionAddMidpoint({loc: loc, edge: edge}, node),
91538                     _t('operations.add.annotation.vertex')
91539                 );
91540
91541                 enterSelectMode(node);
91542             }
91543
91544             function enterSelectMode(node) {
91545                 context.enter(
91546                     modeSelect(context, [node.id]).newFeature(true)
91547                 );
91548             }
91549
91550
91551             function addNode(node) {
91552                 if (Object.keys(defaultTags).length === 0) {
91553                     enterSelectMode(node);
91554                     return;
91555                 }
91556
91557                 var tags = Object.assign({}, node.tags);  // shallow copy
91558                 for (var key in defaultTags) {
91559                     tags[key] = defaultTags[key];
91560                 }
91561
91562                 context.perform(
91563                     actionChangeTags(node.id, tags),
91564                     _t('operations.add.annotation.point')
91565                 );
91566
91567                 enterSelectMode(node);
91568             }
91569
91570
91571             function cancel() {
91572                 context.enter(modeBrowse(context));
91573             }
91574
91575
91576             mode.enter = function() {
91577                 context.install(behavior);
91578             };
91579
91580
91581             mode.exit = function() {
91582                 context.uninstall(behavior);
91583             };
91584
91585
91586             return mode;
91587         }
91588
91589         function modeAddNote(context) {
91590             var mode = {
91591                 id: 'add-note',
91592                 button: 'note',
91593                 title: _t('modes.add_note.title'),
91594                 description: _t('modes.add_note.description'),
91595                 key: _t('modes.add_note.key')
91596             };
91597
91598             var behavior = behaviorDraw(context)
91599                 .on('click', add)
91600                 .on('cancel', cancel)
91601                 .on('finish', cancel);
91602
91603
91604             function add(loc) {
91605                 var osm = services.osm;
91606                 if (!osm) { return; }
91607
91608                 var note = osmNote({ loc: loc, status: 'open', comments: [] });
91609                 osm.replaceNote(note);
91610
91611                 // force a reraw (there is no history change that would otherwise do this)
91612                 context.map().pan([0,0]);
91613
91614                 context
91615                     .selectedNoteID(note.id)
91616                     .enter(modeSelectNote(context, note.id).newFeature(true));
91617             }
91618
91619
91620             function cancel() {
91621                 context.enter(modeBrowse(context));
91622             }
91623
91624
91625             mode.enter = function() {
91626                 context.install(behavior);
91627             };
91628
91629
91630             mode.exit = function() {
91631                 context.uninstall(behavior);
91632             };
91633
91634
91635             return mode;
91636         }
91637
91638         function uiConflicts(context) {
91639             var dispatch$1 = dispatch('cancel', 'save');
91640             var keybinding = utilKeybinding('conflicts');
91641             var _origChanges;
91642             var _conflictList;
91643             var _shownConflictIndex;
91644
91645
91646             function keybindingOn() {
91647                 select(document)
91648                     .call(keybinding.on('⎋', cancel, true));
91649             }
91650
91651             function keybindingOff() {
91652                 select(document)
91653                     .call(keybinding.unbind);
91654             }
91655
91656             function tryAgain() {
91657                 keybindingOff();
91658                 dispatch$1.call('save');
91659             }
91660
91661             function cancel() {
91662                 keybindingOff();
91663                 dispatch$1.call('cancel');
91664             }
91665
91666
91667             function conflicts(selection) {
91668                 keybindingOn();
91669
91670                 var headerEnter = selection.selectAll('.header')
91671                     .data([0])
91672                     .enter()
91673                     .append('div')
91674                     .attr('class', 'header fillL');
91675
91676                 headerEnter
91677                     .append('button')
91678                     .attr('class', 'fr')
91679                     .on('click', cancel)
91680                     .call(svgIcon('#iD-icon-close'));
91681
91682                 headerEnter
91683                     .append('h3')
91684                     .text(_t('save.conflict.header'));
91685
91686                 var bodyEnter = selection.selectAll('.body')
91687                     .data([0])
91688                     .enter()
91689                     .append('div')
91690                     .attr('class', 'body fillL');
91691
91692                 var conflictsHelpEnter = bodyEnter
91693                     .append('div')
91694                     .attr('class', 'conflicts-help')
91695                     .text(_t('save.conflict.help'));
91696
91697
91698                 // Download changes link
91699                 var detected = utilDetect();
91700                 var changeset = new osmChangeset();
91701
91702                 delete changeset.id;  // Export without changeset_id
91703
91704                 var data = JXON.stringify(changeset.osmChangeJXON(_origChanges));
91705                 var blob = new Blob([data], { type: 'text/xml;charset=utf-8;' });
91706                 var fileName = 'changes.osc';
91707
91708                 var linkEnter = conflictsHelpEnter.selectAll('.download-changes')
91709                     .append('a')
91710                     .attr('class', 'download-changes');
91711
91712                 if (detected.download) {      // All except IE11 and Edge
91713                     linkEnter                 // download the data as a file
91714                         .attr('href', window.URL.createObjectURL(blob))
91715                         .attr('download', fileName);
91716
91717                 } else {                      // IE11 and Edge
91718                     linkEnter                 // open data uri in a new tab
91719                         .attr('target', '_blank')
91720                         .on('click.download', function() {
91721                             navigator.msSaveBlob(blob, fileName);
91722                         });
91723                 }
91724
91725                 linkEnter
91726                     .call(svgIcon('#iD-icon-load', 'inline'))
91727                     .append('span')
91728                     .text(_t('save.conflict.download_changes'));
91729
91730
91731                 bodyEnter
91732                     .append('div')
91733                     .attr('class', 'conflict-container fillL3')
91734                     .call(showConflict, 0);
91735
91736                 bodyEnter
91737                     .append('div')
91738                     .attr('class', 'conflicts-done')
91739                     .attr('opacity', 0)
91740                     .style('display', 'none')
91741                     .text(_t('save.conflict.done'));
91742
91743                 var buttonsEnter = bodyEnter
91744                     .append('div')
91745                     .attr('class','buttons col12 joined conflicts-buttons');
91746
91747                 buttonsEnter
91748                     .append('button')
91749                     .attr('disabled', _conflictList.length > 1)
91750                     .attr('class', 'action conflicts-button col6')
91751                     .text(_t('save.title'))
91752                     .on('click.try_again', tryAgain);
91753
91754                 buttonsEnter
91755                     .append('button')
91756                     .attr('class', 'secondary-action conflicts-button col6')
91757                     .text(_t('confirm.cancel'))
91758                     .on('click.cancel', cancel);
91759             }
91760
91761
91762             function showConflict(selection, index) {
91763                 index = utilWrap(index, _conflictList.length);
91764                 _shownConflictIndex = index;
91765
91766                 var parent = select(selection.node().parentNode);
91767
91768                 // enable save button if this is the last conflict being reviewed..
91769                 if (index === _conflictList.length - 1) {
91770                     window.setTimeout(function() {
91771                         parent.select('.conflicts-button')
91772                             .attr('disabled', null);
91773
91774                         parent.select('.conflicts-done')
91775                             .transition()
91776                             .attr('opacity', 1)
91777                             .style('display', 'block');
91778                     }, 250);
91779                 }
91780
91781                 var conflict = selection
91782                     .selectAll('.conflict')
91783                     .data([_conflictList[index]]);
91784
91785                 conflict.exit()
91786                     .remove();
91787
91788                 var conflictEnter = conflict.enter()
91789                     .append('div')
91790                     .attr('class', 'conflict');
91791
91792                 conflictEnter
91793                     .append('h4')
91794                     .attr('class', 'conflict-count')
91795                     .text(_t('save.conflict.count', { num: index + 1, total: _conflictList.length }));
91796
91797                 conflictEnter
91798                     .append('a')
91799                     .attr('class', 'conflict-description')
91800                     .attr('href', '#')
91801                     .text(function(d) { return d.name; })
91802                     .on('click', function(d) {
91803                         event.preventDefault();
91804                         zoomToEntity(d.id);
91805                     });
91806
91807                 var details = conflictEnter
91808                     .append('div')
91809                     .attr('class', 'conflict-detail-container');
91810
91811                 details
91812                     .append('ul')
91813                     .attr('class', 'conflict-detail-list')
91814                     .selectAll('li')
91815                     .data(function(d) { return d.details || []; })
91816                     .enter()
91817                     .append('li')
91818                     .attr('class', 'conflict-detail-item')
91819                     .html(function(d) { return d; });
91820
91821                 details
91822                     .append('div')
91823                     .attr('class', 'conflict-choices')
91824                     .call(addChoices);
91825
91826                 details
91827                     .append('div')
91828                     .attr('class', 'conflict-nav-buttons joined cf')
91829                     .selectAll('button')
91830                     .data(['previous', 'next'])
91831                     .enter()
91832                     .append('button')
91833                     .text(function(d) { return _t('save.conflict.' + d); })
91834                     .attr('class', 'conflict-nav-button action col6')
91835                     .attr('disabled', function(d, i) {
91836                         return (i === 0 && index === 0) ||
91837                             (i === 1 && index === _conflictList.length - 1) || null;
91838                     })
91839                     .on('click', function(d, i) {
91840                         event.preventDefault();
91841
91842                         var container = parent.selectAll('.conflict-container');
91843                         var sign = (i === 0 ? -1 : 1);
91844
91845                         container
91846                             .selectAll('.conflict')
91847                             .remove();
91848
91849                         container
91850                             .call(showConflict, index + sign);
91851                     });
91852
91853             }
91854
91855
91856             function addChoices(selection) {
91857                 var choices = selection
91858                     .append('ul')
91859                     .attr('class', 'layer-list')
91860                     .selectAll('li')
91861                     .data(function(d) { return d.choices || []; });
91862
91863                 // enter
91864                 var choicesEnter = choices.enter()
91865                     .append('li')
91866                     .attr('class', 'layer');
91867
91868                 var labelEnter = choicesEnter
91869                     .append('label');
91870
91871                 labelEnter
91872                     .append('input')
91873                     .attr('type', 'radio')
91874                     .attr('name', function(d) { return d.id; })
91875                     .on('change', function(d, i) {
91876                         var ul = this.parentNode.parentNode.parentNode;
91877                         ul.__data__.chosen = i;
91878                         choose(ul, d);
91879                     });
91880
91881                 labelEnter
91882                     .append('span')
91883                     .text(function(d) { return d.text; });
91884
91885                 // update
91886                 choicesEnter
91887                     .merge(choices)
91888                     .each(function(d, i) {
91889                         var ul = this.parentNode;
91890                         if (ul.__data__.chosen === i) {
91891                             choose(ul, d);
91892                         }
91893                     });
91894             }
91895
91896
91897             function choose(ul, datum) {
91898                 if (event) { event.preventDefault(); }
91899
91900                 select(ul)
91901                     .selectAll('li')
91902                     .classed('active', function(d) { return d === datum; })
91903                     .selectAll('input')
91904                     .property('checked', function(d) { return d === datum; });
91905
91906                 var extent = geoExtent();
91907                 var entity;
91908
91909                 entity = context.graph().hasEntity(datum.id);
91910                 if (entity) { extent._extend(entity.extent(context.graph())); }
91911
91912                 datum.action();
91913
91914                 entity = context.graph().hasEntity(datum.id);
91915                 if (entity) { extent._extend(entity.extent(context.graph())); }
91916
91917                 zoomToEntity(datum.id, extent);
91918             }
91919
91920
91921             function zoomToEntity(id, extent) {
91922                 context.surface().selectAll('.hover')
91923                     .classed('hover', false);
91924
91925                 var entity = context.graph().hasEntity(id);
91926                 if (entity) {
91927                     if (extent) {
91928                         context.map().trimmedExtent(extent);
91929                     } else {
91930                         context.map().zoomToEase(entity);
91931                     }
91932                     context.surface().selectAll(utilEntityOrMemberSelector([entity.id], context.graph()))
91933                         .classed('hover', true);
91934                 }
91935             }
91936
91937
91938             // The conflict list should be an array of objects like:
91939             // {
91940             //     id: id,
91941             //     name: entityName(local),
91942             //     details: merge.conflicts(),
91943             //     chosen: 1,
91944             //     choices: [
91945             //         choice(id, keepMine, forceLocal),
91946             //         choice(id, keepTheirs, forceRemote)
91947             //     ]
91948             // }
91949             conflicts.conflictList = function(_) {
91950                 if (!arguments.length) { return _conflictList; }
91951                 _conflictList = _;
91952                 return conflicts;
91953             };
91954
91955
91956             conflicts.origChanges = function(_) {
91957                 if (!arguments.length) { return _origChanges; }
91958                 _origChanges = _;
91959                 return conflicts;
91960             };
91961
91962
91963             conflicts.shownEntityIds = function() {
91964                 if (_conflictList && typeof _shownConflictIndex === 'number') {
91965                     return [_conflictList[_shownConflictIndex].id];
91966                 }
91967                 return [];
91968             };
91969
91970
91971             return utilRebind(conflicts, dispatch$1, 'on');
91972         }
91973
91974         function uiConfirm(selection) {
91975             var modalSelection = uiModal(selection);
91976
91977             modalSelection.select('.modal')
91978                 .classed('modal-alert', true);
91979
91980             var section = modalSelection.select('.content');
91981
91982             section.append('div')
91983                 .attr('class', 'modal-section header');
91984
91985             section.append('div')
91986                 .attr('class', 'modal-section message-text');
91987
91988             var buttons = section.append('div')
91989                 .attr('class', 'modal-section buttons cf');
91990
91991
91992             modalSelection.okButton = function() {
91993                 buttons
91994                     .append('button')
91995                     .attr('class', 'button ok-button action')
91996                     .on('click.confirm', function() {
91997                         modalSelection.remove();
91998                     })
91999                     .text(_t('confirm.okay'))
92000                     .node()
92001                     .focus();
92002
92003                 return modalSelection;
92004             };
92005
92006
92007             return modalSelection;
92008         }
92009
92010         function uiChangesetEditor(context) {
92011             var dispatch$1 = dispatch('change');
92012             var formFields = uiFormFields(context);
92013             var commentCombo = uiCombobox(context, 'comment').caseSensitive(true);
92014             var _fieldsArr;
92015             var _tags;
92016             var _changesetID;
92017
92018
92019             function changesetEditor(selection) {
92020                 render(selection);
92021             }
92022
92023
92024             function render(selection) {
92025                 var initial = false;
92026
92027                 if (!_fieldsArr) {
92028                     initial = true;
92029                     var presets = _mainPresetIndex;
92030
92031                     _fieldsArr = [
92032                         uiField(context, presets.field('comment'), null, { show: true, revert: false }),
92033                         uiField(context, presets.field('source'), null, { show: false, revert: false }),
92034                         uiField(context, presets.field('hashtags'), null, { show: false, revert: false }) ];
92035
92036                     _fieldsArr.forEach(function(field) {
92037                         field
92038                             .on('change', function(t, onInput) {
92039                                 dispatch$1.call('change', field, undefined, t, onInput);
92040                             });
92041                     });
92042                 }
92043
92044                 _fieldsArr.forEach(function(field) {
92045                     field
92046                         .tags(_tags);
92047                 });
92048
92049
92050                 selection
92051                     .call(formFields.fieldsArr(_fieldsArr));
92052
92053
92054                 if (initial) {
92055                     var commentField = selection.select('.form-field-comment textarea');
92056                     var commentNode = commentField.node();
92057
92058                     if (commentNode) {
92059                         commentNode.focus();
92060                         commentNode.select();
92061                     }
92062
92063                     // trigger a 'blur' event so that comment field can be cleaned
92064                     // and checked for hashtags, even if retrieved from localstorage
92065                     utilTriggerEvent(commentField, 'blur');
92066
92067                     var osm = context.connection();
92068                     if (osm) {
92069                         osm.userChangesets(function (err, changesets) {
92070                             if (err) { return; }
92071
92072                             var comments = changesets.map(function(changeset) {
92073                                 var comment = changeset.tags.comment;
92074                                 return comment ? { title: comment, value: comment } : null;
92075                             }).filter(Boolean);
92076
92077                             commentField
92078                                 .call(commentCombo
92079                                     .data(utilArrayUniqBy(comments, 'title'))
92080                                 );
92081                         });
92082                     }
92083                 }
92084
92085                 // Add warning if comment mentions Google
92086                 var hasGoogle = _tags.comment.match(/google/i);
92087                 var commentWarning = selection.select('.form-field-comment').selectAll('.comment-warning')
92088                     .data(hasGoogle ? [0] : []);
92089
92090                 commentWarning.exit()
92091                     .transition()
92092                     .duration(200)
92093                     .style('opacity', 0)
92094                     .remove();
92095
92096                 var commentEnter = commentWarning.enter()
92097                     .insert('div', '.tag-reference-body')
92098                     .attr('class', 'field-warning comment-warning')
92099                     .style('opacity', 0);
92100
92101                 commentEnter
92102                     .append('a')
92103                     .attr('target', '_blank')
92104                     .attr('tabindex', -1)
92105                     .call(svgIcon('#iD-icon-alert', 'inline'))
92106                     .attr('href', _t('commit.google_warning_link'))
92107                     .append('span')
92108                     .text(_t('commit.google_warning'));
92109
92110                 commentEnter
92111                     .transition()
92112                     .duration(200)
92113                     .style('opacity', 1);
92114             }
92115
92116
92117             changesetEditor.tags = function(_) {
92118                 if (!arguments.length) { return _tags; }
92119                 _tags = _;
92120                 // Don't reset _fieldsArr here.
92121                 return changesetEditor;
92122             };
92123
92124
92125             changesetEditor.changesetID = function(_) {
92126                 if (!arguments.length) { return _changesetID; }
92127                 if (_changesetID === _) { return changesetEditor; }
92128                 _changesetID = _;
92129                 _fieldsArr = null;
92130                 return changesetEditor;
92131             };
92132
92133
92134             return utilRebind(changesetEditor, dispatch$1, 'on');
92135         }
92136
92137         function uiSectionChanges(context) {
92138             var detected = utilDetect();
92139
92140             var _discardTags = {};
92141             _mainFileFetcher.get('discarded')
92142                 .then(function(d) { _discardTags = d; })
92143                 .catch(function() { /* ignore */ });
92144
92145             var section = uiSection('changes-list', context)
92146                 .title(function() {
92147                     var history = context.history();
92148                     var summary = history.difference().summary();
92149                     return _t('commit.changes', { count: summary.length });
92150                 })
92151                 .disclosureContent(renderDisclosureContent);
92152
92153             function renderDisclosureContent(selection) {
92154                 var history = context.history();
92155                 var summary = history.difference().summary();
92156
92157                 var container = selection.selectAll('.commit-section')
92158                     .data([0]);
92159
92160                 var containerEnter = container.enter()
92161                     .append('div')
92162                     .attr('class', 'commit-section');
92163
92164                 containerEnter
92165                     .append('ul')
92166                     .attr('class', 'changeset-list');
92167
92168                 container = containerEnter
92169                     .merge(container);
92170
92171
92172                 var items = container.select('ul').selectAll('li')
92173                     .data(summary);
92174
92175                 var itemsEnter = items.enter()
92176                     .append('li')
92177                     .attr('class', 'change-item');
92178
92179                 itemsEnter
92180                     .each(function(d) {
92181                         select(this)
92182                             .call(svgIcon('#iD-icon-' + d.entity.geometry(d.graph), 'pre-text ' + d.changeType));
92183                     });
92184
92185                 itemsEnter
92186                     .append('span')
92187                     .attr('class', 'change-type')
92188                     .text(function(d) { return _t('commit.' + d.changeType) + ' '; });
92189
92190                 itemsEnter
92191                     .append('strong')
92192                     .attr('class', 'entity-type')
92193                     .text(function(d) {
92194                         var matched = _mainPresetIndex.match(d.entity, d.graph);
92195                         return (matched && matched.name()) || utilDisplayType(d.entity.id);
92196                     });
92197
92198                 itemsEnter
92199                     .append('span')
92200                     .attr('class', 'entity-name')
92201                     .text(function(d) {
92202                         var name = utilDisplayName(d.entity) || '',
92203                             string = '';
92204                         if (name !== '') {
92205                             string += ':';
92206                         }
92207                         return string += ' ' + name;
92208                     });
92209
92210                 itemsEnter
92211                     .style('opacity', 0)
92212                     .transition()
92213                     .style('opacity', 1);
92214
92215                 items = itemsEnter
92216                     .merge(items);
92217
92218                 items
92219                     .on('mouseover', mouseover)
92220                     .on('mouseout', mouseout)
92221                     .on('click', click);
92222
92223
92224                 // Download changeset link
92225                 var changeset = new osmChangeset().update({ id: undefined });
92226                 var changes = history.changes(actionDiscardTags(history.difference(), _discardTags));
92227
92228                 delete changeset.id;  // Export without chnageset_id
92229
92230                 var data = JXON.stringify(changeset.osmChangeJXON(changes));
92231                 var blob = new Blob([data], {type: 'text/xml;charset=utf-8;'});
92232                 var fileName = 'changes.osc';
92233
92234                 var linkEnter = container.selectAll('.download-changes')
92235                     .data([0])
92236                     .enter()
92237                     .append('a')
92238                     .attr('class', 'download-changes');
92239
92240                 if (detected.download) {      // All except IE11 and Edge
92241                     linkEnter                 // download the data as a file
92242                         .attr('href', window.URL.createObjectURL(blob))
92243                         .attr('download', fileName);
92244
92245                 } else {                      // IE11 and Edge
92246                     linkEnter                 // open data uri in a new tab
92247                         .attr('target', '_blank')
92248                         .on('click.download', function() {
92249                             navigator.msSaveBlob(blob, fileName);
92250                         });
92251                 }
92252
92253                 linkEnter
92254                     .call(svgIcon('#iD-icon-load', 'inline'))
92255                     .append('span')
92256                     .text(_t('commit.download_changes'));
92257
92258
92259                 function mouseover(d) {
92260                     if (d.entity) {
92261                         context.surface().selectAll(
92262                             utilEntityOrMemberSelector([d.entity.id], context.graph())
92263                         ).classed('hover', true);
92264                     }
92265                 }
92266
92267
92268                 function mouseout() {
92269                     context.surface().selectAll('.hover')
92270                         .classed('hover', false);
92271                 }
92272
92273
92274                 function click(change) {
92275                     if (change.changeType !== 'deleted') {
92276                         var entity = change.entity;
92277                         context.map().zoomToEase(entity);
92278                         context.surface().selectAll(utilEntityOrMemberSelector([entity.id], context.graph()))
92279                             .classed('hover', true);
92280                     }
92281                 }
92282             }
92283
92284             return section;
92285         }
92286
92287         function uiCommitWarnings(context) {
92288
92289             function commitWarnings(selection) {
92290                 var issuesBySeverity = context.validator()
92291                     .getIssuesBySeverity({ what: 'edited', where: 'all', includeDisabledRules: true });
92292
92293                 for (var severity in issuesBySeverity) {
92294                     var issues = issuesBySeverity[severity];
92295                     var section = severity + '-section';
92296                     var issueItem = severity + '-item';
92297
92298                     var container = selection.selectAll('.' + section)
92299                         .data(issues.length ? [0] : []);
92300
92301                     container.exit()
92302                         .remove();
92303
92304                     var containerEnter = container.enter()
92305                         .append('div')
92306                         .attr('class', 'modal-section ' + section + ' fillL2');
92307
92308                     containerEnter
92309                         .append('h3')
92310                         .text(severity === 'warning' ? _t('commit.warnings') : _t('commit.errors'));
92311
92312                     containerEnter
92313                         .append('ul')
92314                         .attr('class', 'changeset-list');
92315
92316                     container = containerEnter
92317                         .merge(container);
92318
92319
92320                     var items = container.select('ul').selectAll('li')
92321                         .data(issues, function(d) { return d.id; });
92322
92323                     items.exit()
92324                         .remove();
92325
92326                     var itemsEnter = items.enter()
92327                         .append('li')
92328                         .attr('class', issueItem);
92329
92330                     itemsEnter
92331                         .call(svgIcon('#iD-icon-alert', 'pre-text'));
92332
92333                     itemsEnter
92334                         .append('strong')
92335                         .attr('class', 'issue-message');
92336
92337                     itemsEnter.filter(function(d) { return d.tooltip; })
92338                         .call(uiTooltip()
92339                             .title(function(d) { return d.tooltip; })
92340                             .placement('top')
92341                         );
92342
92343                     items = itemsEnter
92344                         .merge(items);
92345
92346                     items.selectAll('.issue-message')
92347                         .text(function(d) {
92348                             return d.message(context);
92349                         });
92350
92351                     items
92352                         .on('mouseover', function(d) {
92353                             if (d.entityIds) {
92354                                 context.surface().selectAll(
92355                                     utilEntityOrMemberSelector(
92356                                         d.entityIds,
92357                                         context.graph()
92358                                     )
92359                                 ).classed('hover', true);
92360                             }
92361                         })
92362                         .on('mouseout', function() {
92363                             context.surface().selectAll('.hover')
92364                                 .classed('hover', false);
92365                         })
92366                         .on('click', function(d) {
92367                             context.validator().focusIssue(d);
92368                         });
92369                 }
92370             }
92371
92372
92373             return commitWarnings;
92374         }
92375
92376         var readOnlyTags = [
92377             /^changesets_count$/,
92378             /^created_by$/,
92379             /^ideditor:/,
92380             /^imagery_used$/,
92381             /^host$/,
92382             /^locale$/,
92383             /^warnings:/,
92384             /^resolved:/,
92385             /^closed:note$/,
92386             /^closed:keepright$/,
92387             /^closed:improveosm:/,
92388             /^closed:osmose:/
92389         ];
92390
92391         // treat most punctuation (except -, _, +, &) as hashtag delimiters - #4398
92392         // from https://stackoverflow.com/a/25575009
92393         var hashtagRegex = /(#[^\u2000-\u206F\u2E00-\u2E7F\s\\'!"#$%()*,.\/:;<=>?@\[\]^`{|}~]+)/g;
92394
92395
92396         function uiCommit(context) {
92397             var dispatch$1 = dispatch('cancel');
92398             var _userDetails;
92399             var _selection;
92400
92401             var changesetEditor = uiChangesetEditor(context)
92402                 .on('change', changeTags);
92403             var rawTagEditor = uiSectionRawTagEditor('changeset-tag-editor', context)
92404                 .on('change', changeTags)
92405                 .readOnlyTags(readOnlyTags);
92406             var commitChanges = uiSectionChanges(context);
92407             var commitWarnings = uiCommitWarnings(context);
92408
92409
92410             function commit(selection) {
92411                 _selection = selection;
92412
92413                 // Initialize changeset if one does not exist yet.
92414                 if (!context.changeset) { initChangeset(); }
92415
92416                 loadDerivedChangesetTags();
92417
92418                 selection.call(render);
92419             }
92420
92421             function initChangeset() {
92422
92423                 // expire stored comment, hashtags, source after cutoff datetime - #3947 #4899
92424                 var commentDate = +corePreferences('commentDate') || 0;
92425                 var currDate = Date.now();
92426                 var cutoff = 2 * 86400 * 1000;   // 2 days
92427                 if (commentDate > currDate || currDate - commentDate > cutoff) {
92428                     corePreferences('comment', null);
92429                     corePreferences('hashtags', null);
92430                     corePreferences('source', null);
92431                 }
92432
92433                 // load in explicitly-set values, if any
92434                 if (context.defaultChangesetComment()) {
92435                     corePreferences('comment', context.defaultChangesetComment());
92436                     corePreferences('commentDate', Date.now());
92437                 }
92438                 if (context.defaultChangesetSource()) {
92439                     corePreferences('source', context.defaultChangesetSource());
92440                     corePreferences('commentDate', Date.now());
92441                 }
92442                 if (context.defaultChangesetHashtags()) {
92443                     corePreferences('hashtags', context.defaultChangesetHashtags());
92444                     corePreferences('commentDate', Date.now());
92445                 }
92446
92447                 var detected = utilDetect();
92448                 var tags = {
92449                     comment: corePreferences('comment') || '',
92450                     created_by: context.cleanTagValue('iD ' + context.version),
92451                     host: context.cleanTagValue(detected.host),
92452                     locale: context.cleanTagValue(_mainLocalizer.localeCode())
92453                 };
92454
92455                 // call findHashtags initially - this will remove stored
92456                 // hashtags if any hashtags are found in the comment - #4304
92457                 findHashtags(tags, true);
92458
92459                 var hashtags = corePreferences('hashtags');
92460                 if (hashtags) {
92461                     tags.hashtags = hashtags;
92462                 }
92463
92464                 var source = corePreferences('source');
92465                 if (source) {
92466                     tags.source = source;
92467                 }
92468                 var photoOverlaysUsed = context.history().photoOverlaysUsed();
92469                 if (photoOverlaysUsed.length) {
92470                     var sources = (tags.source || '').split(';');
92471
92472                     // include this tag for any photo layer
92473                     if (sources.indexOf('streetlevel imagery') === -1) {
92474                         sources.push('streetlevel imagery');
92475                     }
92476
92477                     // add the photo overlays used during editing as sources
92478                     photoOverlaysUsed.forEach(function(photoOverlay) {
92479                         if (sources.indexOf(photoOverlay) === -1) {
92480                             sources.push(photoOverlay);
92481                         }
92482                     });
92483
92484                     tags.source = context.cleanTagValue(sources.join(';'));
92485                 }
92486
92487                 context.changeset = new osmChangeset({ tags: tags });
92488             }
92489
92490             // Calculates read-only metadata tags based on the user's editing session and applies
92491             // them to the changeset.
92492             function loadDerivedChangesetTags() {
92493
92494                 var osm = context.connection();
92495                 if (!osm) { return; }
92496
92497                 var tags = Object.assign({}, context.changeset.tags);   // shallow copy
92498
92499                 // assign tags for imagery used
92500                 var imageryUsed = context.cleanTagValue(context.history().imageryUsed().join(';'));
92501                 tags.imagery_used = imageryUsed || 'None';
92502
92503                 // assign tags for closed issues and notes
92504                 var osmClosed = osm.getClosedIDs();
92505                 var itemType;
92506                 if (osmClosed.length) {
92507                     tags['closed:note'] = context.cleanTagValue(osmClosed.join(';'));
92508                 }
92509                 if (services.keepRight) {
92510                     var krClosed = services.keepRight.getClosedIDs();
92511                     if (krClosed.length) {
92512                         tags['closed:keepright'] = context.cleanTagValue(krClosed.join(';'));
92513                     }
92514                 }
92515                 if (services.improveOSM) {
92516                     var iOsmClosed = services.improveOSM.getClosedCounts();
92517                     for (itemType in iOsmClosed) {
92518                         tags['closed:improveosm:' + itemType] = context.cleanTagValue(iOsmClosed[itemType].toString());
92519                     }
92520                 }
92521                 if (services.osmose) {
92522                     var osmoseClosed = services.osmose.getClosedCounts();
92523                     for (itemType in osmoseClosed) {
92524                         tags['closed:osmose:' + itemType] = context.cleanTagValue(osmoseClosed[itemType].toString());
92525                     }
92526                 }
92527
92528                 // remove existing issue counts
92529                 for (var key in tags) {
92530                     if (key.match(/(^warnings:)|(^resolved:)/)) {
92531                         delete tags[key];
92532                     }
92533                 }
92534
92535                 function addIssueCounts(issues, prefix) {
92536                     var issuesByType = utilArrayGroupBy(issues, 'type');
92537                     for (var issueType in issuesByType) {
92538                         var issuesOfType = issuesByType[issueType];
92539                         if (issuesOfType[0].subtype) {
92540                             var issuesBySubtype = utilArrayGroupBy(issuesOfType, 'subtype');
92541                             for (var issueSubtype in issuesBySubtype) {
92542                                 var issuesOfSubtype = issuesBySubtype[issueSubtype];
92543                                 tags[prefix + ':' + issueType + ':' + issueSubtype] = context.cleanTagValue(issuesOfSubtype.length.toString());
92544                             }
92545                         } else {
92546                             tags[prefix + ':' + issueType] = context.cleanTagValue(issuesOfType.length.toString());
92547                         }
92548                     }
92549                 }
92550
92551                 // add counts of warnings generated by the user's edits
92552                 var warnings = context.validator()
92553                     .getIssuesBySeverity({ what: 'edited', where: 'all', includeIgnored: true, includeDisabledRules: true }).warning;
92554                 addIssueCounts(warnings, 'warnings');
92555
92556                 // add counts of issues resolved by the user's edits
92557                 var resolvedIssues = context.validator().getResolvedIssues();
92558                 addIssueCounts(resolvedIssues, 'resolved');
92559
92560                 context.changeset = context.changeset.update({ tags: tags });
92561             }
92562
92563             function render(selection) {
92564
92565                 var osm = context.connection();
92566                 if (!osm) { return; }
92567
92568                 var header = selection.selectAll('.header')
92569                     .data([0]);
92570
92571                 var headerTitle = header.enter()
92572                     .append('div')
92573                     .attr('class', 'header fillL header-container');
92574
92575                 headerTitle
92576                     .append('div')
92577                     .attr('class', 'header-block header-block-outer');
92578
92579                 headerTitle
92580                     .append('div')
92581                     .attr('class', 'header-block')
92582                     .append('h3')
92583                     .text(_t('commit.title'));
92584
92585                 headerTitle
92586                     .append('div')
92587                     .attr('class', 'header-block header-block-outer header-block-close')
92588                     .append('button')
92589                     .attr('class', 'close')
92590                     .on('click', function() {
92591                         dispatch$1.call('cancel', this);
92592                     })
92593                     .call(svgIcon('#iD-icon-close'));
92594
92595                 var body = selection.selectAll('.body')
92596                     .data([0]);
92597
92598                 body = body.enter()
92599                     .append('div')
92600                     .attr('class', 'body')
92601                     .merge(body);
92602
92603
92604                 // Changeset Section
92605                 var changesetSection = body.selectAll('.changeset-editor')
92606                     .data([0]);
92607
92608                 changesetSection = changesetSection.enter()
92609                     .append('div')
92610                     .attr('class', 'modal-section changeset-editor')
92611                     .merge(changesetSection);
92612
92613                 changesetSection
92614                     .call(changesetEditor
92615                         .changesetID(context.changeset.id)
92616                         .tags(context.changeset.tags)
92617                     );
92618
92619
92620                 // Warnings
92621                 body.call(commitWarnings);
92622
92623
92624                 // Upload Explanation
92625                 var saveSection = body.selectAll('.save-section')
92626                     .data([0]);
92627
92628                 saveSection = saveSection.enter()
92629                     .append('div')
92630                     .attr('class','modal-section save-section fillL')
92631                     .merge(saveSection);
92632
92633                 var prose = saveSection.selectAll('.commit-info')
92634                     .data([0]);
92635
92636                 if (prose.enter().size()) {   // first time, make sure to update user details in prose
92637                     _userDetails = null;
92638                 }
92639
92640                 prose = prose.enter()
92641                     .append('p')
92642                     .attr('class', 'commit-info')
92643                     .text(_t('commit.upload_explanation'))
92644                     .merge(prose);
92645
92646                 // always check if this has changed, but only update prose.html()
92647                 // if needed, because it can trigger a style recalculation
92648                 osm.userDetails(function(err, user) {
92649                     if (err) { return; }
92650
92651                     if (_userDetails === user) { return; }  // no change
92652                     _userDetails = user;
92653
92654                     var userLink = select(document.createElement('div'));
92655
92656                     if (user.image_url) {
92657                         userLink
92658                             .append('img')
92659                             .attr('src', user.image_url)
92660                             .attr('class', 'icon pre-text user-icon');
92661                     }
92662
92663                     userLink
92664                         .append('a')
92665                         .attr('class', 'user-info')
92666                         .text(user.display_name)
92667                         .attr('href', osm.userURL(user.display_name))
92668                         .attr('target', '_blank');
92669
92670                     prose
92671                         .html(_t('commit.upload_explanation_with_user', { user: userLink.html() }));
92672                 });
92673
92674
92675                 // Request Review
92676                 var requestReview = saveSection.selectAll('.request-review')
92677                     .data([0]);
92678
92679                 // Enter
92680                 var requestReviewEnter = requestReview.enter()
92681                     .append('div')
92682                     .attr('class', 'request-review');
92683
92684                 var requestReviewDomId = utilUniqueDomId('commit-input-request-review');
92685
92686                 var labelEnter = requestReviewEnter
92687                     .append('label')
92688                     .attr('for', requestReviewDomId);
92689
92690                 labelEnter
92691                     .append('input')
92692                     .attr('type', 'checkbox')
92693                     .attr('id', requestReviewDomId);
92694
92695                 labelEnter
92696                     .append('span')
92697                     .text(_t('commit.request_review'));
92698
92699                 // Update
92700                 requestReview = requestReview
92701                     .merge(requestReviewEnter);
92702
92703                 var requestReviewInput = requestReview.selectAll('input')
92704                     .property('checked', isReviewRequested(context.changeset.tags))
92705                     .on('change', toggleRequestReview);
92706
92707
92708                 // Buttons
92709                 var buttonSection = saveSection.selectAll('.buttons')
92710                     .data([0]);
92711
92712                 // enter
92713                 var buttonEnter = buttonSection.enter()
92714                     .append('div')
92715                     .attr('class', 'buttons fillL');
92716
92717                 buttonEnter
92718                     .append('button')
92719                     .attr('class', 'secondary-action button cancel-button')
92720                     .append('span')
92721                     .attr('class', 'label')
92722                     .text(_t('commit.cancel'));
92723
92724                 var uploadButton = buttonEnter
92725                     .append('button')
92726                     .attr('class', 'action button save-button');
92727
92728                 uploadButton.append('span')
92729                     .attr('class', 'label')
92730                     .text(_t('commit.save'));
92731
92732                 var uploadBlockerTooltipText = getUploadBlockerMessage();
92733
92734                 // update
92735                 buttonSection = buttonSection
92736                     .merge(buttonEnter);
92737
92738                 buttonSection.selectAll('.cancel-button')
92739                     .on('click.cancel', function() {
92740                         dispatch$1.call('cancel', this);
92741                     });
92742
92743                 buttonSection.selectAll('.save-button')
92744                     .classed('disabled', uploadBlockerTooltipText !== null)
92745                     .on('click.save', function() {
92746                         if (!select(this).classed('disabled')) {
92747                             this.blur();    // avoid keeping focus on the button - #4641
92748                             context.uploader().save(context.changeset);
92749                         }
92750                     });
92751
92752                 // remove any existing tooltip
92753                 uiTooltip().destroyAny(buttonSection.selectAll('.save-button'));
92754
92755                 if (uploadBlockerTooltipText) {
92756                     buttonSection.selectAll('.save-button')
92757                         .call(uiTooltip().title(uploadBlockerTooltipText).placement('top'));
92758                 }
92759
92760                 // Raw Tag Editor
92761                 var tagSection = body.selectAll('.tag-section.raw-tag-editor')
92762                     .data([0]);
92763
92764                 tagSection = tagSection.enter()
92765                     .append('div')
92766                     .attr('class', 'modal-section tag-section raw-tag-editor')
92767                     .merge(tagSection);
92768
92769                 tagSection
92770                     .call(rawTagEditor
92771                         .tags(Object.assign({}, context.changeset.tags))   // shallow copy
92772                         .render
92773                     );
92774
92775                 var changesSection = body.selectAll('.commit-changes-section')
92776                     .data([0]);
92777
92778                 changesSection = changesSection.enter()
92779                     .append('div')
92780                     .attr('class', 'modal-section commit-changes-section')
92781                     .merge(changesSection);
92782
92783                 // Change summary
92784                 changesSection.call(commitChanges.render);
92785
92786
92787                 function toggleRequestReview() {
92788                     var rr = requestReviewInput.property('checked');
92789                     updateChangeset({ review_requested: (rr ? 'yes' : undefined) });
92790
92791                     tagSection
92792                         .call(rawTagEditor
92793                             .tags(Object.assign({}, context.changeset.tags))   // shallow copy
92794                             .render
92795                         );
92796                 }
92797             }
92798
92799
92800             function getUploadBlockerMessage() {
92801                 var errors = context.validator()
92802                     .getIssuesBySeverity({ what: 'edited', where: 'all' }).error;
92803
92804                 if (errors.length) {
92805                     return _t('commit.outstanding_errors_message', { count: errors.length });
92806
92807                 } else {
92808                     var hasChangesetComment = context.changeset && context.changeset.tags.comment && context.changeset.tags.comment.trim().length;
92809                     if (!hasChangesetComment) {
92810                         return _t('commit.comment_needed_message');
92811                     }
92812                 }
92813                 return null;
92814             }
92815
92816
92817             function changeTags(_, changed, onInput) {
92818                 if (changed.hasOwnProperty('comment')) {
92819                     if (changed.comment === undefined) {
92820                         changed.comment = '';
92821                     }
92822                     if (!onInput) {
92823                         corePreferences('comment', changed.comment);
92824                         corePreferences('commentDate', Date.now());
92825                     }
92826                 }
92827                 if (changed.hasOwnProperty('source')) {
92828                     if (changed.source === undefined) {
92829                         corePreferences('source', null);
92830                     } else if (!onInput) {
92831                         corePreferences('source', changed.source);
92832                         corePreferences('commentDate', Date.now());
92833                     }
92834                 }
92835                 // no need to update `prefs` for `hashtags` here since it's done in `updateChangeset`
92836
92837                 updateChangeset(changed, onInput);
92838
92839                 if (_selection) {
92840                     _selection.call(render);
92841                 }
92842             }
92843
92844
92845             function findHashtags(tags, commentOnly) {
92846                 var detectedHashtags = commentHashtags();
92847
92848                 if (detectedHashtags.length) {
92849                     // always remove stored hashtags if there are hashtags in the comment - #4304
92850                     corePreferences('hashtags', null);
92851                 }
92852                 if (!detectedHashtags.length || !commentOnly) {
92853                     detectedHashtags = detectedHashtags.concat(hashtagHashtags());
92854                 }
92855
92856                 var allLowerCase = new Set();
92857                 return detectedHashtags.filter(function(hashtag) {
92858                     // Compare tags as lowercase strings, but keep original case tags
92859                     var lowerCase = hashtag.toLowerCase();
92860                     if (!allLowerCase.has(lowerCase)) {
92861                         allLowerCase.add(lowerCase);
92862                         return true;
92863                     }
92864                     return false;
92865                 });
92866
92867                 // Extract hashtags from `comment`
92868                 function commentHashtags() {
92869                     var matches = (tags.comment || '')
92870                         .replace(/http\S*/g, '')  // drop anything that looks like a URL - #4289
92871                         .match(hashtagRegex);
92872
92873                     return matches || [];
92874                 }
92875
92876                 // Extract and clean hashtags from `hashtags`
92877                 function hashtagHashtags() {
92878                     var matches = (tags.hashtags || '')
92879                         .split(/[,;\s]+/)
92880                         .map(function (s) {
92881                             if (s[0] !== '#') { s = '#' + s; }    // prepend '#'
92882                             var matched = s.match(hashtagRegex);
92883                             return matched && matched[0];
92884                         }).filter(Boolean);                       // exclude falsy
92885
92886                     return matches || [];
92887                 }
92888             }
92889
92890
92891             function isReviewRequested(tags) {
92892                 var rr = tags.review_requested;
92893                 if (rr === undefined) { return false; }
92894                 rr = rr.trim().toLowerCase();
92895                 return !(rr === '' || rr === 'no');
92896             }
92897
92898
92899             function updateChangeset(changed, onInput) {
92900                 var tags = Object.assign({}, context.changeset.tags);   // shallow copy
92901
92902                 Object.keys(changed).forEach(function(k) {
92903                     var v = changed[k];
92904                     k = context.cleanTagKey(k);
92905                     if (readOnlyTags.indexOf(k) !== -1) { return; }
92906
92907                     if (k !== '' && v !== undefined) {
92908                         if (onInput) {
92909                             tags[k] = v;
92910                         } else {
92911                             tags[k] = context.cleanTagValue(v);
92912                         }
92913                     } else {
92914                         delete tags[k];
92915                     }
92916                 });
92917
92918                 if (!onInput) {
92919                     // when changing the comment, override hashtags with any found in comment.
92920                     var commentOnly = changed.hasOwnProperty('comment') && (changed.comment !== '');
92921                     var arr = findHashtags(tags, commentOnly);
92922                     if (arr.length) {
92923                         tags.hashtags = context.cleanTagValue(arr.join(';'));
92924                         corePreferences('hashtags', tags.hashtags);
92925                     } else {
92926                         delete tags.hashtags;
92927                         corePreferences('hashtags', null);
92928                     }
92929                 }
92930
92931                 // always update userdetails, just in case user reauthenticates as someone else
92932                 if (_userDetails && _userDetails.changesets_count !== undefined) {
92933                     var changesetsCount = parseInt(_userDetails.changesets_count, 10) + 1;  // #4283
92934                     tags.changesets_count = String(changesetsCount);
92935
92936                     // first 100 edits - new user
92937                     if (changesetsCount <= 100) {
92938                         var s;
92939                         s = corePreferences('walkthrough_completed');
92940                         if (s) {
92941                             tags['ideditor:walkthrough_completed'] = s;
92942                         }
92943
92944                         s = corePreferences('walkthrough_progress');
92945                         if (s) {
92946                             tags['ideditor:walkthrough_progress'] = s;
92947                         }
92948
92949                         s = corePreferences('walkthrough_started');
92950                         if (s) {
92951                             tags['ideditor:walkthrough_started'] = s;
92952                         }
92953                     }
92954                 } else {
92955                     delete tags.changesets_count;
92956                 }
92957
92958                 if (!fastDeepEqual(context.changeset.tags, tags)) {
92959                     context.changeset = context.changeset.update({ tags: tags });
92960                 }
92961             }
92962
92963
92964             commit.reset = function() {
92965                 context.changeset = null;
92966             };
92967
92968
92969             return utilRebind(commit, dispatch$1, 'on');
92970         }
92971
92972         var RADIUS = 6378137;
92973         var FLATTENING = 1/298.257223563;
92974         var POLAR_RADIUS$1 = 6356752.3142;
92975
92976         var wgs84 = {
92977                 RADIUS: RADIUS,
92978                 FLATTENING: FLATTENING,
92979                 POLAR_RADIUS: POLAR_RADIUS$1
92980         };
92981
92982         var geometry_1 = geometry;
92983         var ring = ringArea;
92984
92985         function geometry(_) {
92986             var area = 0, i;
92987             switch (_.type) {
92988                 case 'Polygon':
92989                     return polygonArea(_.coordinates);
92990                 case 'MultiPolygon':
92991                     for (i = 0; i < _.coordinates.length; i++) {
92992                         area += polygonArea(_.coordinates[i]);
92993                     }
92994                     return area;
92995                 case 'Point':
92996                 case 'MultiPoint':
92997                 case 'LineString':
92998                 case 'MultiLineString':
92999                     return 0;
93000                 case 'GeometryCollection':
93001                     for (i = 0; i < _.geometries.length; i++) {
93002                         area += geometry(_.geometries[i]);
93003                     }
93004                     return area;
93005             }
93006         }
93007
93008         function polygonArea(coords) {
93009             var area = 0;
93010             if (coords && coords.length > 0) {
93011                 area += Math.abs(ringArea(coords[0]));
93012                 for (var i = 1; i < coords.length; i++) {
93013                     area -= Math.abs(ringArea(coords[i]));
93014                 }
93015             }
93016             return area;
93017         }
93018
93019         /**
93020          * Calculate the approximate area of the polygon were it projected onto
93021          *     the earth.  Note that this area will be positive if ring is oriented
93022          *     clockwise, otherwise it will be negative.
93023          *
93024          * Reference:
93025          * Robert. G. Chamberlain and William H. Duquette, "Some Algorithms for
93026          *     Polygons on a Sphere", JPL Publication 07-03, Jet Propulsion
93027          *     Laboratory, Pasadena, CA, June 2007 http://trs-new.jpl.nasa.gov/dspace/handle/2014/40409
93028          *
93029          * Returns:
93030          * {float} The approximate signed geodesic area of the polygon in square
93031          *     meters.
93032          */
93033
93034         function ringArea(coords) {
93035             var p1, p2, p3, lowerIndex, middleIndex, upperIndex, i,
93036             area = 0,
93037             coordsLength = coords.length;
93038
93039             if (coordsLength > 2) {
93040                 for (i = 0; i < coordsLength; i++) {
93041                     if (i === coordsLength - 2) {// i = N-2
93042                         lowerIndex = coordsLength - 2;
93043                         middleIndex = coordsLength -1;
93044                         upperIndex = 0;
93045                     } else if (i === coordsLength - 1) {// i = N-1
93046                         lowerIndex = coordsLength - 1;
93047                         middleIndex = 0;
93048                         upperIndex = 1;
93049                     } else { // i = 0 to N-3
93050                         lowerIndex = i;
93051                         middleIndex = i+1;
93052                         upperIndex = i+2;
93053                     }
93054                     p1 = coords[lowerIndex];
93055                     p2 = coords[middleIndex];
93056                     p3 = coords[upperIndex];
93057                     area += ( rad(p3[0]) - rad(p1[0]) ) * Math.sin( rad(p2[1]));
93058                 }
93059
93060                 area = area * wgs84.RADIUS * wgs84.RADIUS / 2;
93061             }
93062
93063             return area;
93064         }
93065
93066         function rad(_) {
93067             return _ * Math.PI / 180;
93068         }
93069
93070         var geojsonArea = {
93071                 geometry: geometry_1,
93072                 ring: ring
93073         };
93074
93075         function toRadians(angleInDegrees) {
93076           return (angleInDegrees * Math.PI) / 180;
93077         }
93078
93079         function toDegrees(angleInRadians) {
93080           return (angleInRadians * 180) / Math.PI;
93081         }
93082
93083         function offset(c1, distance, bearing) {
93084           var lat1 = toRadians(c1[1]);
93085           var lon1 = toRadians(c1[0]);
93086           var dByR = distance / 6378137; // distance divided by 6378137 (radius of the earth) wgs84
93087           var lat = Math.asin(
93088             Math.sin(lat1) * Math.cos(dByR) +
93089               Math.cos(lat1) * Math.sin(dByR) * Math.cos(bearing)
93090           );
93091           var lon =
93092             lon1 +
93093             Math.atan2(
93094               Math.sin(bearing) * Math.sin(dByR) * Math.cos(lat1),
93095               Math.cos(dByR) - Math.sin(lat1) * Math.sin(lat)
93096             );
93097           return [toDegrees(lon), toDegrees(lat)];
93098         }
93099
93100         function validateCenter(center) {
93101           var validCenterLengths = [2, 3];
93102           if (!Array.isArray(center) || !validCenterLengths.includes(center.length)) {
93103             throw new Error("ERROR! Center has to be an array of length two or three");
93104           }
93105           var lng = center[0];
93106           var lat = center[1];
93107           if (typeof lng !== "number" || typeof lat !== "number") {
93108             throw new Error(
93109               ("ERROR! Longitude and Latitude has to be numbers but where " + (typeof lng) + " and " + (typeof lat))
93110             );
93111           }
93112           if (lng > 180 || lng < -180) {
93113             throw new Error(
93114               ("ERROR! Longitude has to be between -180 and 180 but was " + lng)
93115             );
93116           }
93117
93118           if (lat > 90 || lat < -90) {
93119             throw new Error(
93120               ("ERROR! Latitude has to be between -90 and 90 but was " + lat)
93121             );
93122           }
93123         }
93124
93125         function validateRadius(radius) {
93126           if (typeof radius !== "number") {
93127             throw new Error(
93128               ("ERROR! Radius has to be a positive number but was: " + (typeof radius))
93129             );
93130           }
93131
93132           if (radius <= 0) {
93133             throw new Error(
93134               ("ERROR! Radius has to be a positive number but was: " + radius)
93135             );
93136           }
93137         }
93138
93139         function validateNumberOfSegments(numberOfSegments) {
93140           if (typeof numberOfSegments !== "number" && numberOfSegments !== undefined) {
93141             throw new Error(
93142               ("ERROR! Number of segments has to be a number but was: " + (typeof numberOfSegments))
93143             );
93144           }
93145
93146           if (numberOfSegments < 3) {
93147             throw new Error(
93148               ("ERROR! Number of segments has to be at least 3 but was: " + numberOfSegments)
93149             );
93150           }
93151         }
93152
93153         function validateInput(ref) {
93154           var center = ref.center;
93155           var radius = ref.radius;
93156           var numberOfSegments = ref.numberOfSegments;
93157
93158           validateCenter(center);
93159           validateRadius(radius);
93160           validateNumberOfSegments(numberOfSegments);
93161         }
93162
93163         var circleToPolygon = function circleToPolygon(center, radius, numberOfSegments) {
93164           var n = numberOfSegments ? numberOfSegments : 32;
93165
93166           // validateInput() throws error on invalid input and do nothing on valid input
93167           validateInput({ center: center, radius: radius, numberOfSegments: numberOfSegments });
93168
93169           var coordinates = [];
93170           for (var i = 0; i < n; ++i) {
93171             coordinates.push(offset(center, radius, (2 * Math.PI * -i) / n));
93172           }
93173           coordinates.push(coordinates[0]);
93174
93175           return {
93176             type: "Polygon",
93177             coordinates: [coordinates]
93178           };
93179         };
93180
93181         var geojsonPrecision = createCommonjsModule(function (module) {
93182         (function() {
93183
93184           function parse(t, coordinatePrecision, extrasPrecision) {
93185
93186             function point(p) {
93187               return p.map(function(e, index) {
93188                 if (index < 2) {
93189                     return 1 * e.toFixed(coordinatePrecision);
93190                 } else {
93191                     return 1 * e.toFixed(extrasPrecision);
93192                 }
93193               });
93194             }
93195
93196             function multi(l) {
93197               return l.map(point);
93198             }
93199
93200             function poly(p) {
93201               return p.map(multi);
93202             }
93203
93204             function multiPoly(m) {
93205               return m.map(poly);
93206             }
93207
93208             function geometry(obj) {
93209               if (!obj) {
93210                 return {};
93211               }
93212               
93213               switch (obj.type) {
93214                 case "Point":
93215                   obj.coordinates = point(obj.coordinates);
93216                   return obj;
93217                 case "LineString":
93218                 case "MultiPoint":
93219                   obj.coordinates = multi(obj.coordinates);
93220                   return obj;
93221                 case "Polygon":
93222                 case "MultiLineString":
93223                   obj.coordinates = poly(obj.coordinates);
93224                   return obj;
93225                 case "MultiPolygon":
93226                   obj.coordinates = multiPoly(obj.coordinates);
93227                   return obj;
93228                 case "GeometryCollection":
93229                   obj.geometries = obj.geometries.map(geometry);
93230                   return obj;
93231                 default :
93232                   return {};
93233               }
93234             }
93235
93236             function feature(obj) {
93237               obj.geometry = geometry(obj.geometry);
93238               return obj
93239             }
93240
93241             function featureCollection(f) {
93242               f.features = f.features.map(feature);
93243               return f;
93244             }
93245
93246             function geometryCollection(g) {
93247               g.geometries = g.geometries.map(geometry);
93248               return g;
93249             }
93250
93251             if (!t) {
93252               return t;
93253             }
93254
93255             switch (t.type) {
93256               case "Feature":
93257                 return feature(t);
93258               case "GeometryCollection" :
93259                 return geometryCollection(t);
93260               case "FeatureCollection" :
93261                 return featureCollection(t);
93262               case "Point":
93263               case "LineString":
93264               case "Polygon":
93265               case "MultiPoint":
93266               case "MultiPolygon":
93267               case "MultiLineString":
93268                 return geometry(t);
93269               default :
93270                 return t;
93271             }
93272               
93273           }
93274
93275           module.exports = parse;
93276           module.exports.parse = parse;
93277
93278         }());
93279         });
93280
93281         /* Polyfill service v3.13.0
93282          * For detailed credits and licence information see http://github.com/financial-times/polyfill-service
93283          *
93284          * - Array.prototype.fill, License: CC0 */
93285
93286         if (!('fill' in Array.prototype)) {
93287           Object.defineProperty(Array.prototype, 'fill', {
93288             configurable: true,
93289             value: function fill (value) {
93290               if (this === undefined || this === null) {
93291                 throw new TypeError(this + ' is not an object')
93292               }
93293
93294               var arrayLike = Object(this);
93295
93296               var length = Math.max(Math.min(arrayLike.length, 9007199254740991), 0) || 0;
93297
93298               var relativeStart = 1 in arguments ? parseInt(Number(arguments[1]), 10) || 0 : 0;
93299
93300               relativeStart = relativeStart < 0 ? Math.max(length + relativeStart, 0) : Math.min(relativeStart, length);
93301
93302               var relativeEnd = 2 in arguments && arguments[2] !== undefined ? parseInt(Number(arguments[2]), 10) || 0 : length;
93303
93304               relativeEnd = relativeEnd < 0 ? Math.max(length + arguments[2], 0) : Math.min(relativeEnd, length);
93305
93306               while (relativeStart < relativeEnd) {
93307                 arrayLike[relativeStart] = value;
93308
93309                 ++relativeStart;
93310               }
93311
93312               return arrayLike
93313             },
93314             writable: true
93315           });
93316         }
93317
93318         /**
93319          * Polyfill for IE support
93320          */
93321         Number.isFinite = Number.isFinite || function (value) {
93322           return typeof value === 'number' && isFinite(value)
93323         };
93324
93325         Number.isInteger = Number.isInteger || function (val) {
93326           return typeof val === 'number' &&
93327           isFinite(val) &&
93328           Math.floor(val) === val
93329         };
93330
93331         Number.parseFloat = Number.parseFloat || parseFloat;
93332
93333         Number.isNaN = Number.isNaN || function (value) {
93334           return value !== value // eslint-disable-line
93335         };
93336
93337         /**
93338          * Polyfill for IE support
93339          */
93340         Math.trunc = Math.trunc || function (x) {
93341           return x < 0 ? Math.ceil(x) : Math.floor(x)
93342         };
93343
93344         var NumberUtil = function NumberUtil () {};
93345
93346         NumberUtil.prototype.interfaces_ = function interfaces_ () {
93347           return []
93348         };
93349         NumberUtil.prototype.getClass = function getClass () {
93350           return NumberUtil
93351         };
93352         NumberUtil.prototype.equalsWithTolerance = function equalsWithTolerance (x1, x2, tolerance) {
93353           return Math.abs(x1 - x2) <= tolerance
93354         };
93355
93356         var IllegalArgumentException = (function (Error) {
93357                 function IllegalArgumentException (message) {
93358                         Error.call(this, message);
93359                         this.name = 'IllegalArgumentException';
93360                         this.message = message;
93361                         this.stack = (new Error()).stack;
93362                 }
93363
93364                 if ( Error ) { IllegalArgumentException.__proto__ = Error; }
93365                 IllegalArgumentException.prototype = Object.create( Error && Error.prototype );
93366                 IllegalArgumentException.prototype.constructor = IllegalArgumentException;
93367
93368                 return IllegalArgumentException;
93369         }(Error));
93370
93371         var Double = function Double () {};
93372
93373         var staticAccessors$1 = { MAX_VALUE: { configurable: true } };
93374
93375         Double.isNaN = function isNaN (n) { return Number.isNaN(n) };
93376         Double.doubleToLongBits = function doubleToLongBits (n) { return n };
93377         Double.longBitsToDouble = function longBitsToDouble (n) { return n };
93378         Double.isInfinite = function isInfinite (n) { return !Number.isFinite(n) };
93379         staticAccessors$1.MAX_VALUE.get = function () { return Number.MAX_VALUE };
93380
93381         Object.defineProperties( Double, staticAccessors$1 );
93382
93383         var Comparable = function Comparable () {};
93384
93385         var Clonable = function Clonable () {};
93386
93387         var Comparator = function Comparator () {};
93388
93389         function Serializable () {}
93390
93391         // import Assert from '../util/Assert'
93392
93393         var Coordinate = function Coordinate () {
93394           this.x = null;
93395           this.y = null;
93396           this.z = null;
93397           if (arguments.length === 0) {
93398             this.x = 0.0;
93399             this.y = 0.0;
93400             this.z = Coordinate.NULL_ORDINATE;
93401           } else if (arguments.length === 1) {
93402             var c = arguments[0];
93403             this.x = c.x;
93404             this.y = c.y;
93405             this.z = c.z;
93406           } else if (arguments.length === 2) {
93407             this.x = arguments[0];
93408             this.y = arguments[1];
93409             this.z = Coordinate.NULL_ORDINATE;
93410           } else if (arguments.length === 3) {
93411             this.x = arguments[0];
93412             this.y = arguments[1];
93413             this.z = arguments[2];
93414           }
93415         };
93416
93417         var staticAccessors = { DimensionalComparator: { configurable: true },serialVersionUID: { configurable: true },NULL_ORDINATE: { configurable: true },X: { configurable: true },Y: { configurable: true },Z: { configurable: true } };
93418         Coordinate.prototype.setOrdinate = function setOrdinate (ordinateIndex, value) {
93419           switch (ordinateIndex) {
93420             case Coordinate.X:
93421               this.x = value;
93422               break
93423             case Coordinate.Y:
93424               this.y = value;
93425               break
93426             case Coordinate.Z:
93427               this.z = value;
93428               break
93429             default:
93430               throw new IllegalArgumentException('Invalid ordinate index: ' + ordinateIndex)
93431           }
93432         };
93433         Coordinate.prototype.equals2D = function equals2D () {
93434           if (arguments.length === 1) {
93435             var other = arguments[0];
93436             if (this.x !== other.x) {
93437               return false
93438             }
93439             if (this.y !== other.y) {
93440               return false
93441             }
93442             return true
93443           } else if (arguments.length === 2) {
93444             var c = arguments[0];
93445             var tolerance = arguments[1];
93446             if (!NumberUtil.equalsWithTolerance(this.x, c.x, tolerance)) {
93447               return false
93448             }
93449             if (!NumberUtil.equalsWithTolerance(this.y, c.y, tolerance)) {
93450               return false
93451             }
93452             return true
93453           }
93454         };
93455         Coordinate.prototype.getOrdinate = function getOrdinate (ordinateIndex) {
93456           switch (ordinateIndex) {
93457             case Coordinate.X:
93458               return this.x
93459             case Coordinate.Y:
93460               return this.y
93461             case Coordinate.Z:
93462               return this.z
93463           }
93464           throw new IllegalArgumentException('Invalid ordinate index: ' + ordinateIndex)
93465         };
93466         Coordinate.prototype.equals3D = function equals3D (other) {
93467           return this.x === other.x &&
93468                  this.y === other.y &&
93469                  ((this.z === other.z || Double.isNaN(this.z)) &&
93470                  Double.isNaN(other.z))
93471         };
93472         Coordinate.prototype.equals = function equals (other) {
93473           if (!(other instanceof Coordinate)) {
93474             return false
93475           }
93476           return this.equals2D(other)
93477         };
93478         Coordinate.prototype.equalInZ = function equalInZ (c, tolerance) {
93479           return NumberUtil.equalsWithTolerance(this.z, c.z, tolerance)
93480         };
93481         Coordinate.prototype.compareTo = function compareTo (o) {
93482           var other = o;
93483           if (this.x < other.x) { return -1 }
93484           if (this.x > other.x) { return 1 }
93485           if (this.y < other.y) { return -1 }
93486           if (this.y > other.y) { return 1 }
93487           return 0
93488         };
93489         Coordinate.prototype.clone = function clone () {
93490           // try {
93491           // var coord = null
93492           // return coord
93493           // } catch (e) {
93494           // if (e instanceof CloneNotSupportedException) {
93495           //   Assert.shouldNeverReachHere("this shouldn't happen because this class is Cloneable")
93496           //   return null
93497           // } else throw e
93498           // } finally {}
93499         };
93500         Coordinate.prototype.copy = function copy () {
93501           return new Coordinate(this)
93502         };
93503         Coordinate.prototype.toString = function toString () {
93504           return '(' + this.x + ', ' + this.y + ', ' + this.z + ')'
93505         };
93506         Coordinate.prototype.distance3D = function distance3D (c) {
93507           var dx = this.x - c.x;
93508           var dy = this.y - c.y;
93509           var dz = this.z - c.z;
93510           return Math.sqrt(dx * dx + dy * dy + dz * dz)
93511         };
93512         Coordinate.prototype.distance = function distance (c) {
93513           var dx = this.x - c.x;
93514           var dy = this.y - c.y;
93515           return Math.sqrt(dx * dx + dy * dy)
93516         };
93517         Coordinate.prototype.hashCode = function hashCode () {
93518           var result = 17;
93519           result = 37 * result + Coordinate.hashCode(this.x);
93520           result = 37 * result + Coordinate.hashCode(this.y);
93521           return result
93522         };
93523         Coordinate.prototype.setCoordinate = function setCoordinate (other) {
93524           this.x = other.x;
93525           this.y = other.y;
93526           this.z = other.z;
93527         };
93528         Coordinate.prototype.interfaces_ = function interfaces_ () {
93529           return [Comparable, Clonable, Serializable]
93530         };
93531         Coordinate.prototype.getClass = function getClass () {
93532           return Coordinate
93533         };
93534         Coordinate.hashCode = function hashCode () {
93535           if (arguments.length === 1) {
93536             var x = arguments[0];
93537             var f = Double.doubleToLongBits(x);
93538             return Math.trunc((f ^ f) >>> 32)
93539           }
93540         };
93541         staticAccessors.DimensionalComparator.get = function () { return DimensionalComparator };
93542         staticAccessors.serialVersionUID.get = function () { return 6683108902428366910 };
93543         staticAccessors.NULL_ORDINATE.get = function () { return Double.NaN };
93544         staticAccessors.X.get = function () { return 0 };
93545         staticAccessors.Y.get = function () { return 1 };
93546         staticAccessors.Z.get = function () { return 2 };
93547
93548         Object.defineProperties( Coordinate, staticAccessors );
93549
93550         var DimensionalComparator = function DimensionalComparator (dimensionsToTest) {
93551           this._dimensionsToTest = 2;
93552           if (arguments.length === 0) ; else if (arguments.length === 1) {
93553             var dimensionsToTest$1 = arguments[0];
93554             if (dimensionsToTest$1 !== 2 && dimensionsToTest$1 !== 3) { throw new IllegalArgumentException('only 2 or 3 dimensions may be specified') }
93555             this._dimensionsToTest = dimensionsToTest$1;
93556           }
93557         };
93558         DimensionalComparator.prototype.compare = function compare (o1, o2) {
93559           var c1 = o1;
93560           var c2 = o2;
93561           var compX = DimensionalComparator.compare(c1.x, c2.x);
93562           if (compX !== 0) { return compX }
93563           var compY = DimensionalComparator.compare(c1.y, c2.y);
93564           if (compY !== 0) { return compY }
93565           if (this._dimensionsToTest <= 2) { return 0 }
93566           var compZ = DimensionalComparator.compare(c1.z, c2.z);
93567           return compZ
93568         };
93569         DimensionalComparator.prototype.interfaces_ = function interfaces_ () {
93570           return [Comparator]
93571         };
93572         DimensionalComparator.prototype.getClass = function getClass () {
93573           return DimensionalComparator
93574         };
93575         DimensionalComparator.compare = function compare (a, b) {
93576           if (a < b) { return -1 }
93577           if (a > b) { return 1 }
93578           if (Double.isNaN(a)) {
93579             if (Double.isNaN(b)) { return 0 }
93580             return -1
93581           }
93582           if (Double.isNaN(b)) { return 1 }
93583           return 0
93584         };
93585
93586         // import hasInterface from '../../../../hasInterface'
93587         // import CoordinateSequence from './CoordinateSequence'
93588
93589         var CoordinateSequenceFactory = function CoordinateSequenceFactory () {};
93590
93591         CoordinateSequenceFactory.prototype.create = function create () {
93592           // if (arguments.length === 1) {
93593           // if (arguments[0] instanceof Array) {
93594           //   let coordinates = arguments[0]
93595           // } else if (hasInterface(arguments[0], CoordinateSequence)) {
93596           //   let coordSeq = arguments[0]
93597           // }
93598           // } else if (arguments.length === 2) {
93599           // let size = arguments[0]
93600           // let dimension = arguments[1]
93601           // }
93602         };
93603         CoordinateSequenceFactory.prototype.interfaces_ = function interfaces_ () {
93604           return []
93605         };
93606         CoordinateSequenceFactory.prototype.getClass = function getClass () {
93607           return CoordinateSequenceFactory
93608         };
93609
93610         var Location = function Location () {};
93611
93612         var staticAccessors$4 = { INTERIOR: { configurable: true },BOUNDARY: { configurable: true },EXTERIOR: { configurable: true },NONE: { configurable: true } };
93613
93614         Location.prototype.interfaces_ = function interfaces_ () {
93615           return []
93616         };
93617         Location.prototype.getClass = function getClass () {
93618           return Location
93619         };
93620         Location.toLocationSymbol = function toLocationSymbol (locationValue) {
93621           switch (locationValue) {
93622             case Location.EXTERIOR:
93623               return 'e'
93624             case Location.BOUNDARY:
93625               return 'b'
93626             case Location.INTERIOR:
93627               return 'i'
93628             case Location.NONE:
93629               return '-'
93630           }
93631           throw new IllegalArgumentException('Unknown location value: ' + locationValue)
93632         };
93633         staticAccessors$4.INTERIOR.get = function () { return 0 };
93634         staticAccessors$4.BOUNDARY.get = function () { return 1 };
93635         staticAccessors$4.EXTERIOR.get = function () { return 2 };
93636         staticAccessors$4.NONE.get = function () { return -1 };
93637
93638         Object.defineProperties( Location, staticAccessors$4 );
93639
93640         var hasInterface = function (o, i) {
93641           return o.interfaces_ && o.interfaces_().indexOf(i) > -1
93642         };
93643
93644         var MathUtil = function MathUtil () {};
93645
93646         var staticAccessors$5 = { LOG_10: { configurable: true } };
93647
93648         MathUtil.prototype.interfaces_ = function interfaces_ () {
93649           return []
93650         };
93651         MathUtil.prototype.getClass = function getClass () {
93652           return MathUtil
93653         };
93654         MathUtil.log10 = function log10 (x) {
93655           var ln = Math.log(x);
93656           if (Double.isInfinite(ln)) { return ln }
93657           if (Double.isNaN(ln)) { return ln }
93658           return ln / MathUtil.LOG_10
93659         };
93660         MathUtil.min = function min (v1, v2, v3, v4) {
93661           var min = v1;
93662           if (v2 < min) { min = v2; }
93663           if (v3 < min) { min = v3; }
93664           if (v4 < min) { min = v4; }
93665           return min
93666         };
93667         MathUtil.clamp = function clamp () {
93668           if (typeof arguments[2] === 'number' && (typeof arguments[0] === 'number' && typeof arguments[1] === 'number')) {
93669             var x = arguments[0];
93670             var min = arguments[1];
93671             var max = arguments[2];
93672             if (x < min) { return min }
93673             if (x > max) { return max }
93674             return x
93675           } else if (Number.isInteger(arguments[2]) && (Number.isInteger(arguments[0]) && Number.isInteger(arguments[1]))) {
93676             var x$1 = arguments[0];
93677             var min$1 = arguments[1];
93678             var max$1 = arguments[2];
93679             if (x$1 < min$1) { return min$1 }
93680             if (x$1 > max$1) { return max$1 }
93681             return x$1
93682           }
93683         };
93684         MathUtil.wrap = function wrap (index, max) {
93685           if (index < 0) {
93686             return max - -index % max
93687           }
93688           return index % max
93689         };
93690         MathUtil.max = function max () {
93691           if (arguments.length === 3) {
93692             var v1 = arguments[0];
93693             var v2 = arguments[1];
93694             var v3 = arguments[2];
93695             var max = v1;
93696             if (v2 > max) { max = v2; }
93697             if (v3 > max) { max = v3; }
93698             return max
93699           } else if (arguments.length === 4) {
93700             var v1$1 = arguments[0];
93701             var v2$1 = arguments[1];
93702             var v3$1 = arguments[2];
93703             var v4 = arguments[3];
93704             var max$1 = v1$1;
93705             if (v2$1 > max$1) { max$1 = v2$1; }
93706             if (v3$1 > max$1) { max$1 = v3$1; }
93707             if (v4 > max$1) { max$1 = v4; }
93708             return max$1
93709           }
93710         };
93711         MathUtil.average = function average (x1, x2) {
93712           return (x1 + x2) / 2.0
93713         };
93714         staticAccessors$5.LOG_10.get = function () { return Math.log(10) };
93715
93716         Object.defineProperties( MathUtil, staticAccessors$5 );
93717
93718         var StringBuffer = function StringBuffer (str) {
93719           this.str = str;
93720         };
93721         StringBuffer.prototype.append = function append (e) {
93722           this.str += e;
93723         };
93724
93725         StringBuffer.prototype.setCharAt = function setCharAt (i, c) {
93726           this.str = this.str.substr(0, i) + c + this.str.substr(i + 1);
93727         };
93728
93729         StringBuffer.prototype.toString = function toString (e) {
93730           return this.str
93731         };
93732
93733         var Integer = function Integer (value) {
93734           this.value = value;
93735         };
93736         Integer.prototype.intValue = function intValue () {
93737           return this.value
93738         };
93739         Integer.prototype.compareTo = function compareTo (o) {
93740           if (this.value < o) { return -1 }
93741           if (this.value > o) { return 1 }
93742           return 0
93743         };
93744         Integer.isNaN = function isNaN (n) { return Number.isNaN(n) };
93745
93746         var Character = function Character () {};
93747
93748         Character.isWhitespace = function isWhitespace (c) { return ((c <= 32 && c >= 0) || c === 127) };
93749         Character.toUpperCase = function toUpperCase (c) { return c.toUpperCase() };
93750
93751         var DD = function DD () {
93752           this._hi = 0.0;
93753           this._lo = 0.0;
93754           if (arguments.length === 0) {
93755             this.init(0.0);
93756           } else if (arguments.length === 1) {
93757             if (typeof arguments[0] === 'number') {
93758               var x = arguments[0];
93759               this.init(x);
93760             } else if (arguments[0] instanceof DD) {
93761               var dd = arguments[0];
93762               this.init(dd);
93763             } else if (typeof arguments[0] === 'string') {
93764               var str = arguments[0];
93765               DD.call(this, DD.parse(str));
93766             }
93767           } else if (arguments.length === 2) {
93768             var hi = arguments[0];
93769             var lo = arguments[1];
93770             this.init(hi, lo);
93771           }
93772         };
93773
93774         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 } };
93775         DD.prototype.le = function le (y) {
93776           return (this._hi < y._hi || this._hi === y._hi) && this._lo <= y._lo
93777         };
93778         DD.prototype.extractSignificantDigits = function extractSignificantDigits (insertDecimalPoint, magnitude) {
93779           var y = this.abs();
93780           var mag = DD.magnitude(y._hi);
93781           var scale = DD.TEN.pow(mag);
93782           y = y.divide(scale);
93783           if (y.gt(DD.TEN)) {
93784             y = y.divide(DD.TEN);
93785             mag += 1;
93786           } else if (y.lt(DD.ONE)) {
93787             y = y.multiply(DD.TEN);
93788             mag -= 1;
93789           }
93790           var decimalPointPos = mag + 1;
93791           var buf = new StringBuffer();
93792           var numDigits = DD.MAX_PRINT_DIGITS - 1;
93793           for (var i = 0; i <= numDigits; i++) {
93794             if (insertDecimalPoint && i === decimalPointPos) {
93795               buf.append('.');
93796             }
93797             var digit = Math.trunc(y._hi);
93798             if (digit < 0) {
93799               break
93800             }
93801             var rebiasBy10 = false;
93802             var digitChar = 0;
93803             if (digit > 9) {
93804               rebiasBy10 = true;
93805               digitChar = '9';
93806             } else {
93807               digitChar = '0' + digit;
93808             }
93809             buf.append(digitChar);
93810             y = y.subtract(DD.valueOf(digit)).multiply(DD.TEN);
93811             if (rebiasBy10) { y.selfAdd(DD.TEN); }
93812             var continueExtractingDigits = true;
93813             var remMag = DD.magnitude(y._hi);
93814             if (remMag < 0 && Math.abs(remMag) >= numDigits - i) { continueExtractingDigits = false; }
93815             if (!continueExtractingDigits) { break }
93816           }
93817           magnitude[0] = mag;
93818           return buf.toString()
93819         };
93820         DD.prototype.sqr = function sqr () {
93821           return this.multiply(this)
93822         };
93823         DD.prototype.doubleValue = function doubleValue () {
93824           return this._hi + this._lo
93825         };
93826         DD.prototype.subtract = function subtract () {
93827           if (arguments[0] instanceof DD) {
93828             var y = arguments[0];
93829             return this.add(y.negate())
93830           } else if (typeof arguments[0] === 'number') {
93831             var y$1 = arguments[0];
93832             return this.add(-y$1)
93833           }
93834         };
93835         DD.prototype.equals = function equals () {
93836           if (arguments.length === 1) {
93837             var y = arguments[0];
93838             return this._hi === y._hi && this._lo === y._lo
93839           }
93840         };
93841         DD.prototype.isZero = function isZero () {
93842           return this._hi === 0.0 && this._lo === 0.0
93843         };
93844         DD.prototype.selfSubtract = function selfSubtract () {
93845           if (arguments[0] instanceof DD) {
93846             var y = arguments[0];
93847             if (this.isNaN()) { return this }
93848             return this.selfAdd(-y._hi, -y._lo)
93849           } else if (typeof arguments[0] === 'number') {
93850             var y$1 = arguments[0];
93851             if (this.isNaN()) { return this }
93852             return this.selfAdd(-y$1, 0.0)
93853           }
93854         };
93855         DD.prototype.getSpecialNumberString = function getSpecialNumberString () {
93856           if (this.isZero()) { return '0.0' }
93857           if (this.isNaN()) { return 'NaN ' }
93858           return null
93859         };
93860         DD.prototype.min = function min (x) {
93861           if (this.le(x)) {
93862             return this
93863           } else {
93864             return x
93865           }
93866         };
93867         DD.prototype.selfDivide = function selfDivide () {
93868           if (arguments.length === 1) {
93869             if (arguments[0] instanceof DD) {
93870               var y = arguments[0];
93871               return this.selfDivide(y._hi, y._lo)
93872             } else if (typeof arguments[0] === 'number') {
93873               var y$1 = arguments[0];
93874               return this.selfDivide(y$1, 0.0)
93875             }
93876           } else if (arguments.length === 2) {
93877             var yhi = arguments[0];
93878             var ylo = arguments[1];
93879             var hc = null;
93880             var tc = null;
93881             var hy = null;
93882             var ty = null;
93883             var C = null;
93884             var c = null;
93885             var U = null;
93886             var u = null;
93887             C = this._hi / yhi;
93888             c = DD.SPLIT * C;
93889             hc = c - C;
93890             u = DD.SPLIT * yhi;
93891             hc = c - hc;
93892             tc = C - hc;
93893             hy = u - yhi;
93894             U = C * yhi;
93895             hy = u - hy;
93896             ty = yhi - hy;
93897             u = hc * hy - U + hc * ty + tc * hy + tc * ty;
93898             c = (this._hi - U - u + this._lo - C * ylo) / yhi;
93899             u = C + c;
93900             this._hi = u;
93901             this._lo = C - u + c;
93902             return this
93903           }
93904         };
93905         DD.prototype.dump = function dump () {
93906           return 'DD<' + this._hi + ', ' + this._lo + '>'
93907         };
93908         DD.prototype.divide = function divide () {
93909           if (arguments[0] instanceof DD) {
93910             var y = arguments[0];
93911             var hc = null;
93912             var tc = null;
93913             var hy = null;
93914             var ty = null;
93915             var C = null;
93916             var c = null;
93917             var U = null;
93918             var u = null;
93919             C = this._hi / y._hi;
93920             c = DD.SPLIT * C;
93921             hc = c - C;
93922             u = DD.SPLIT * y._hi;
93923             hc = c - hc;
93924             tc = C - hc;
93925             hy = u - y._hi;
93926             U = C * y._hi;
93927             hy = u - hy;
93928             ty = y._hi - hy;
93929             u = hc * hy - U + hc * ty + tc * hy + tc * ty;
93930             c = (this._hi - U - u + this._lo - C * y._lo) / y._hi;
93931             u = C + c;
93932             var zhi = u;
93933             var zlo = C - u + c;
93934             return new DD(zhi, zlo)
93935           } else if (typeof arguments[0] === 'number') {
93936             var y$1 = arguments[0];
93937             if (Double.isNaN(y$1)) { return DD.createNaN() }
93938             return DD.copy(this).selfDivide(y$1, 0.0)
93939           }
93940         };
93941         DD.prototype.ge = function ge (y) {
93942           return (this._hi > y._hi || this._hi === y._hi) && this._lo >= y._lo
93943         };
93944         DD.prototype.pow = function pow (exp) {
93945           if (exp === 0.0) { return DD.valueOf(1.0) }
93946           var r = new DD(this);
93947           var s = DD.valueOf(1.0);
93948           var n = Math.abs(exp);
93949           if (n > 1) {
93950             while (n > 0) {
93951               if (n % 2 === 1) {
93952                 s.selfMultiply(r);
93953               }
93954               n /= 2;
93955               if (n > 0) { r = r.sqr(); }
93956             }
93957           } else {
93958             s = r;
93959           }
93960           if (exp < 0) { return s.reciprocal() }
93961           return s
93962         };
93963         DD.prototype.ceil = function ceil () {
93964           if (this.isNaN()) { return DD.NaN }
93965           var fhi = Math.ceil(this._hi);
93966           var flo = 0.0;
93967           if (fhi === this._hi) {
93968             flo = Math.ceil(this._lo);
93969           }
93970           return new DD(fhi, flo)
93971         };
93972         DD.prototype.compareTo = function compareTo (o) {
93973           var other = o;
93974           if (this._hi < other._hi) { return -1 }
93975           if (this._hi > other._hi) { return 1 }
93976           if (this._lo < other._lo) { return -1 }
93977           if (this._lo > other._lo) { return 1 }
93978           return 0
93979         };
93980         DD.prototype.rint = function rint () {
93981           if (this.isNaN()) { return this }
93982           var plus5 = this.add(0.5);
93983           return plus5.floor()
93984         };
93985         DD.prototype.setValue = function setValue () {
93986           if (arguments[0] instanceof DD) {
93987             var value = arguments[0];
93988             this.init(value);
93989             return this
93990           } else if (typeof arguments[0] === 'number') {
93991             var value$1 = arguments[0];
93992             this.init(value$1);
93993             return this
93994           }
93995         };
93996         DD.prototype.max = function max (x) {
93997           if (this.ge(x)) {
93998             return this
93999           } else {
94000             return x
94001           }
94002         };
94003         DD.prototype.sqrt = function sqrt () {
94004           if (this.isZero()) { return DD.valueOf(0.0) }
94005           if (this.isNegative()) {
94006             return DD.NaN
94007           }
94008           var x = 1.0 / Math.sqrt(this._hi);
94009           var ax = this._hi * x;
94010           var axdd = DD.valueOf(ax);
94011           var diffSq = this.subtract(axdd.sqr());
94012           var d2 = diffSq._hi * (x * 0.5);
94013           return axdd.add(d2)
94014         };
94015         DD.prototype.selfAdd = function selfAdd () {
94016           if (arguments.length === 1) {
94017             if (arguments[0] instanceof DD) {
94018               var y = arguments[0];
94019               return this.selfAdd(y._hi, y._lo)
94020             } else if (typeof arguments[0] === 'number') {
94021               var y$1 = arguments[0];
94022               var H = null;
94023               var h = null;
94024               var S = null;
94025               var s = null;
94026               var e = null;
94027               var f = null;
94028               S = this._hi + y$1;
94029               e = S - this._hi;
94030               s = S - e;
94031               s = y$1 - e + (this._hi - s);
94032               f = s + this._lo;
94033               H = S + f;
94034               h = f + (S - H);
94035               this._hi = H + h;
94036               this._lo = h + (H - this._hi);
94037               return this
94038             }
94039           } else if (arguments.length === 2) {
94040             var yhi = arguments[0];
94041             var ylo = arguments[1];
94042             var H$1 = null;
94043             var h$1 = null;
94044             var T = null;
94045             var t = null;
94046             var S$1 = null;
94047             var s$1 = null;
94048             var e$1 = null;
94049             var f$1 = null;
94050             S$1 = this._hi + yhi;
94051             T = this._lo + ylo;
94052             e$1 = S$1 - this._hi;
94053             f$1 = T - this._lo;
94054             s$1 = S$1 - e$1;
94055             t = T - f$1;
94056             s$1 = yhi - e$1 + (this._hi - s$1);
94057             t = ylo - f$1 + (this._lo - t);
94058             e$1 = s$1 + T;
94059             H$1 = S$1 + e$1;
94060             h$1 = e$1 + (S$1 - H$1);
94061             e$1 = t + h$1;
94062             var zhi = H$1 + e$1;
94063             var zlo = e$1 + (H$1 - zhi);
94064             this._hi = zhi;
94065             this._lo = zlo;
94066             return this
94067           }
94068         };
94069         DD.prototype.selfMultiply = function selfMultiply () {
94070           if (arguments.length === 1) {
94071             if (arguments[0] instanceof DD) {
94072               var y = arguments[0];
94073               return this.selfMultiply(y._hi, y._lo)
94074             } else if (typeof arguments[0] === 'number') {
94075               var y$1 = arguments[0];
94076               return this.selfMultiply(y$1, 0.0)
94077             }
94078           } else if (arguments.length === 2) {
94079             var yhi = arguments[0];
94080             var ylo = arguments[1];
94081             var hx = null;
94082             var tx = null;
94083             var hy = null;
94084             var ty = null;
94085             var C = null;
94086             var c = null;
94087             C = DD.SPLIT * this._hi;
94088             hx = C - this._hi;
94089             c = DD.SPLIT * yhi;
94090             hx = C - hx;
94091             tx = this._hi - hx;
94092             hy = c - yhi;
94093             C = this._hi * yhi;
94094             hy = c - hy;
94095             ty = yhi - hy;
94096             c = hx * hy - C + hx * ty + tx * hy + tx * ty + (this._hi * ylo + this._lo * yhi);
94097             var zhi = C + c;
94098             hx = C - zhi;
94099             var zlo = c + hx;
94100             this._hi = zhi;
94101             this._lo = zlo;
94102             return this
94103           }
94104         };
94105         DD.prototype.selfSqr = function selfSqr () {
94106           return this.selfMultiply(this)
94107         };
94108         DD.prototype.floor = function floor () {
94109           if (this.isNaN()) { return DD.NaN }
94110           var fhi = Math.floor(this._hi);
94111           var flo = 0.0;
94112           if (fhi === this._hi) {
94113             flo = Math.floor(this._lo);
94114           }
94115           return new DD(fhi, flo)
94116         };
94117         DD.prototype.negate = function negate () {
94118           if (this.isNaN()) { return this }
94119           return new DD(-this._hi, -this._lo)
94120         };
94121         DD.prototype.clone = function clone () {
94122           // try {
94123           // return null
94124           // } catch (ex) {
94125           // if (ex instanceof CloneNotSupportedException) {
94126           //   return null
94127           // } else throw ex
94128           // } finally {}
94129         };
94130         DD.prototype.multiply = function multiply () {
94131           if (arguments[0] instanceof DD) {
94132             var y = arguments[0];
94133             if (y.isNaN()) { return DD.createNaN() }
94134             return DD.copy(this).selfMultiply(y)
94135           } else if (typeof arguments[0] === 'number') {
94136             var y$1 = arguments[0];
94137             if (Double.isNaN(y$1)) { return DD.createNaN() }
94138             return DD.copy(this).selfMultiply(y$1, 0.0)
94139           }
94140         };
94141         DD.prototype.isNaN = function isNaN () {
94142           return Double.isNaN(this._hi)
94143         };
94144         DD.prototype.intValue = function intValue () {
94145           return Math.trunc(this._hi)
94146         };
94147         DD.prototype.toString = function toString () {
94148           var mag = DD.magnitude(this._hi);
94149           if (mag >= -3 && mag <= 20) { return this.toStandardNotation() }
94150           return this.toSciNotation()
94151         };
94152         DD.prototype.toStandardNotation = function toStandardNotation () {
94153           var specialStr = this.getSpecialNumberString();
94154           if (specialStr !== null) { return specialStr }
94155           var magnitude = new Array(1).fill(null);
94156           var sigDigits = this.extractSignificantDigits(true, magnitude);
94157           var decimalPointPos = magnitude[0] + 1;
94158           var num = sigDigits;
94159           if (sigDigits.charAt(0) === '.') {
94160             num = '0' + sigDigits;
94161           } else if (decimalPointPos < 0) {
94162             num = '0.' + DD.stringOfChar('0', -decimalPointPos) + sigDigits;
94163           } else if (sigDigits.indexOf('.') === -1) {
94164             var numZeroes = decimalPointPos - sigDigits.length;
94165             var zeroes = DD.stringOfChar('0', numZeroes);
94166             num = sigDigits + zeroes + '.0';
94167           }
94168           if (this.isNegative()) { return '-' + num }
94169           return num
94170         };
94171         DD.prototype.reciprocal = function reciprocal () {
94172           var hc = null;
94173           var tc = null;
94174           var hy = null;
94175           var ty = null;
94176           var C = null;
94177           var c = null;
94178           var U = null;
94179           var u = null;
94180           C = 1.0 / this._hi;
94181           c = DD.SPLIT * C;
94182           hc = c - C;
94183           u = DD.SPLIT * this._hi;
94184           hc = c - hc;
94185           tc = C - hc;
94186           hy = u - this._hi;
94187           U = C * this._hi;
94188           hy = u - hy;
94189           ty = this._hi - hy;
94190           u = hc * hy - U + hc * ty + tc * hy + tc * ty;
94191           c = (1.0 - U - u - C * this._lo) / this._hi;
94192           var zhi = C + c;
94193           var zlo = C - zhi + c;
94194           return new DD(zhi, zlo)
94195         };
94196         DD.prototype.toSciNotation = function toSciNotation () {
94197           if (this.isZero()) { return DD.SCI_NOT_ZERO }
94198           var specialStr = this.getSpecialNumberString();
94199           if (specialStr !== null) { return specialStr }
94200           var magnitude = new Array(1).fill(null);
94201           var digits = this.extractSignificantDigits(false, magnitude);
94202           var expStr = DD.SCI_NOT_EXPONENT_CHAR + magnitude[0];
94203           if (digits.charAt(0) === '0') {
94204             throw new Error('Found leading zero: ' + digits)
94205           }
94206           var trailingDigits = '';
94207           if (digits.length > 1) { trailingDigits = digits.substring(1); }
94208           var digitsWithDecimal = digits.charAt(0) + '.' + trailingDigits;
94209           if (this.isNegative()) { return '-' + digitsWithDecimal + expStr }
94210           return digitsWithDecimal + expStr
94211         };
94212         DD.prototype.abs = function abs () {
94213           if (this.isNaN()) { return DD.NaN }
94214           if (this.isNegative()) { return this.negate() }
94215           return new DD(this)
94216         };
94217         DD.prototype.isPositive = function isPositive () {
94218           return (this._hi > 0.0 || this._hi === 0.0) && this._lo > 0.0
94219         };
94220         DD.prototype.lt = function lt (y) {
94221           return (this._hi < y._hi || this._hi === y._hi) && this._lo < y._lo
94222         };
94223         DD.prototype.add = function add () {
94224           if (arguments[0] instanceof DD) {
94225             var y = arguments[0];
94226             return DD.copy(this).selfAdd(y)
94227           } else if (typeof arguments[0] === 'number') {
94228             var y$1 = arguments[0];
94229             return DD.copy(this).selfAdd(y$1)
94230           }
94231         };
94232         DD.prototype.init = function init () {
94233           if (arguments.length === 1) {
94234             if (typeof arguments[0] === 'number') {
94235               var x = arguments[0];
94236               this._hi = x;
94237               this._lo = 0.0;
94238             } else if (arguments[0] instanceof DD) {
94239               var dd = arguments[0];
94240               this._hi = dd._hi;
94241               this._lo = dd._lo;
94242             }
94243           } else if (arguments.length === 2) {
94244             var hi = arguments[0];
94245             var lo = arguments[1];
94246             this._hi = hi;
94247             this._lo = lo;
94248           }
94249         };
94250         DD.prototype.gt = function gt (y) {
94251           return (this._hi > y._hi || this._hi === y._hi) && this._lo > y._lo
94252         };
94253         DD.prototype.isNegative = function isNegative () {
94254           return (this._hi < 0.0 || this._hi === 0.0) && this._lo < 0.0
94255         };
94256         DD.prototype.trunc = function trunc () {
94257           if (this.isNaN()) { return DD.NaN }
94258           if (this.isPositive()) { return this.floor(); } else { return this.ceil() }
94259         };
94260         DD.prototype.signum = function signum () {
94261           if (this._hi > 0) { return 1 }
94262           if (this._hi < 0) { return -1 }
94263           if (this._lo > 0) { return 1 }
94264           if (this._lo < 0) { return -1 }
94265           return 0
94266         };
94267         DD.prototype.interfaces_ = function interfaces_ () {
94268           return [Serializable, Comparable, Clonable]
94269         };
94270         DD.prototype.getClass = function getClass () {
94271           return DD
94272         };
94273         DD.sqr = function sqr (x) {
94274           return DD.valueOf(x).selfMultiply(x)
94275         };
94276         DD.valueOf = function valueOf () {
94277           if (typeof arguments[0] === 'string') {
94278             var str = arguments[0];
94279             return DD.parse(str)
94280           } else if (typeof arguments[0] === 'number') {
94281             var x = arguments[0];
94282             return new DD(x)
94283           }
94284         };
94285         DD.sqrt = function sqrt (x) {
94286           return DD.valueOf(x).sqrt()
94287         };
94288         DD.parse = function parse (str) {
94289           var i = 0;
94290           var strlen = str.length;
94291           while (Character.isWhitespace(str.charAt(i))) { i++; }
94292           var isNegative = false;
94293           if (i < strlen) {
94294             var signCh = str.charAt(i);
94295             if (signCh === '-' || signCh === '+') {
94296               i++;
94297               if (signCh === '-') { isNegative = true; }
94298             }
94299           }
94300           var val = new DD();
94301           var numDigits = 0;
94302           var numBeforeDec = 0;
94303           var exp = 0;
94304           while (true) {
94305             if (i >= strlen) { break }
94306             var ch = str.charAt(i);
94307             i++;
94308             if (Character.isDigit(ch)) {
94309               var d = ch - '0';
94310               val.selfMultiply(DD.TEN);
94311               val.selfAdd(d);
94312               numDigits++;
94313               continue
94314             }
94315             if (ch === '.') {
94316               numBeforeDec = numDigits;
94317               continue
94318             }
94319             if (ch === 'e' || ch === 'E') {
94320               var expStr = str.substring(i);
94321               try {
94322                 exp = Integer.parseInt(expStr);
94323               } catch (ex) {
94324                 if (ex instanceof Error) {
94325                   throw new Error('Invalid exponent ' + expStr + ' in string ' + str)
94326                 } else { throw ex }
94327               } finally {}
94328               break
94329             }
94330             throw new Error("Unexpected character '" + ch + "' at position " + i + ' in string ' + str)
94331           }
94332           var val2 = val;
94333           var numDecPlaces = numDigits - numBeforeDec - exp;
94334           if (numDecPlaces === 0) {
94335             val2 = val;
94336           } else if (numDecPlaces > 0) {
94337             var scale = DD.TEN.pow(numDecPlaces);
94338             val2 = val.divide(scale);
94339           } else if (numDecPlaces < 0) {
94340             var scale$1 = DD.TEN.pow(-numDecPlaces);
94341             val2 = val.multiply(scale$1);
94342           }
94343           if (isNegative) {
94344             return val2.negate()
94345           }
94346           return val2
94347         };
94348         DD.createNaN = function createNaN () {
94349           return new DD(Double.NaN, Double.NaN)
94350         };
94351         DD.copy = function copy (dd) {
94352           return new DD(dd)
94353         };
94354         DD.magnitude = function magnitude (x) {
94355           var xAbs = Math.abs(x);
94356           var xLog10 = Math.log(xAbs) / Math.log(10);
94357           var xMag = Math.trunc(Math.floor(xLog10));
94358           var xApprox = Math.pow(10, xMag);
94359           if (xApprox * 10 <= xAbs) { xMag += 1; }
94360           return xMag
94361         };
94362         DD.stringOfChar = function stringOfChar (ch, len) {
94363           var buf = new StringBuffer();
94364           for (var i = 0; i < len; i++) {
94365             buf.append(ch);
94366           }
94367           return buf.toString()
94368         };
94369         staticAccessors$7.PI.get = function () { return new DD(3.141592653589793116e+00, 1.224646799147353207e-16) };
94370         staticAccessors$7.TWO_PI.get = function () { return new DD(6.283185307179586232e+00, 2.449293598294706414e-16) };
94371         staticAccessors$7.PI_2.get = function () { return new DD(1.570796326794896558e+00, 6.123233995736766036e-17) };
94372         staticAccessors$7.E.get = function () { return new DD(2.718281828459045091e+00, 1.445646891729250158e-16) };
94373         staticAccessors$7.NaN.get = function () { return new DD(Double.NaN, Double.NaN) };
94374         staticAccessors$7.EPS.get = function () { return 1.23259516440783e-32 };
94375         staticAccessors$7.SPLIT.get = function () { return 134217729.0 };
94376         staticAccessors$7.MAX_PRINT_DIGITS.get = function () { return 32 };
94377         staticAccessors$7.TEN.get = function () { return DD.valueOf(10.0) };
94378         staticAccessors$7.ONE.get = function () { return DD.valueOf(1.0) };
94379         staticAccessors$7.SCI_NOT_EXPONENT_CHAR.get = function () { return 'E' };
94380         staticAccessors$7.SCI_NOT_ZERO.get = function () { return '0.0E0' };
94381
94382         Object.defineProperties( DD, staticAccessors$7 );
94383
94384         var CGAlgorithmsDD = function CGAlgorithmsDD () {};
94385
94386         var staticAccessors$6 = { DP_SAFE_EPSILON: { configurable: true } };
94387
94388         CGAlgorithmsDD.prototype.interfaces_ = function interfaces_ () {
94389           return []
94390         };
94391         CGAlgorithmsDD.prototype.getClass = function getClass () {
94392           return CGAlgorithmsDD
94393         };
94394         CGAlgorithmsDD.orientationIndex = function orientationIndex (p1, p2, q) {
94395           var index = CGAlgorithmsDD.orientationIndexFilter(p1, p2, q);
94396           if (index <= 1) { return index }
94397           var dx1 = DD.valueOf(p2.x).selfAdd(-p1.x);
94398           var dy1 = DD.valueOf(p2.y).selfAdd(-p1.y);
94399           var dx2 = DD.valueOf(q.x).selfAdd(-p2.x);
94400           var dy2 = DD.valueOf(q.y).selfAdd(-p2.y);
94401           return dx1.selfMultiply(dy2).selfSubtract(dy1.selfMultiply(dx2)).signum()
94402         };
94403         CGAlgorithmsDD.signOfDet2x2 = function signOfDet2x2 (x1, y1, x2, y2) {
94404           var det = x1.multiply(y2).selfSubtract(y1.multiply(x2));
94405           return det.signum()
94406         };
94407         CGAlgorithmsDD.intersection = function intersection (p1, p2, q1, q2) {
94408           var denom1 = DD.valueOf(q2.y).selfSubtract(q1.y).selfMultiply(DD.valueOf(p2.x).selfSubtract(p1.x));
94409           var denom2 = DD.valueOf(q2.x).selfSubtract(q1.x).selfMultiply(DD.valueOf(p2.y).selfSubtract(p1.y));
94410           var denom = denom1.subtract(denom2);
94411           var numx1 = DD.valueOf(q2.x).selfSubtract(q1.x).selfMultiply(DD.valueOf(p1.y).selfSubtract(q1.y));
94412           var numx2 = DD.valueOf(q2.y).selfSubtract(q1.y).selfMultiply(DD.valueOf(p1.x).selfSubtract(q1.x));
94413           var numx = numx1.subtract(numx2);
94414           var fracP = numx.selfDivide(denom).doubleValue();
94415           var x = DD.valueOf(p1.x).selfAdd(DD.valueOf(p2.x).selfSubtract(p1.x).selfMultiply(fracP)).doubleValue();
94416           var numy1 = DD.valueOf(p2.x).selfSubtract(p1.x).selfMultiply(DD.valueOf(p1.y).selfSubtract(q1.y));
94417           var numy2 = DD.valueOf(p2.y).selfSubtract(p1.y).selfMultiply(DD.valueOf(p1.x).selfSubtract(q1.x));
94418           var numy = numy1.subtract(numy2);
94419           var fracQ = numy.selfDivide(denom).doubleValue();
94420           var y = DD.valueOf(q1.y).selfAdd(DD.valueOf(q2.y).selfSubtract(q1.y).selfMultiply(fracQ)).doubleValue();
94421           return new Coordinate(x, y)
94422         };
94423         CGAlgorithmsDD.orientationIndexFilter = function orientationIndexFilter (pa, pb, pc) {
94424           var detsum = null;
94425           var detleft = (pa.x - pc.x) * (pb.y - pc.y);
94426           var detright = (pa.y - pc.y) * (pb.x - pc.x);
94427           var det = detleft - detright;
94428           if (detleft > 0.0) {
94429             if (detright <= 0.0) {
94430               return CGAlgorithmsDD.signum(det)
94431             } else {
94432               detsum = detleft + detright;
94433             }
94434           } else if (detleft < 0.0) {
94435             if (detright >= 0.0) {
94436               return CGAlgorithmsDD.signum(det)
94437             } else {
94438               detsum = -detleft - detright;
94439             }
94440           } else {
94441             return CGAlgorithmsDD.signum(det)
94442           }
94443           var errbound = CGAlgorithmsDD.DP_SAFE_EPSILON * detsum;
94444           if (det >= errbound || -det >= errbound) {
94445             return CGAlgorithmsDD.signum(det)
94446           }
94447           return 2
94448         };
94449         CGAlgorithmsDD.signum = function signum (x) {
94450           if (x > 0) { return 1 }
94451           if (x < 0) { return -1 }
94452           return 0
94453         };
94454         staticAccessors$6.DP_SAFE_EPSILON.get = function () { return 1e-15 };
94455
94456         Object.defineProperties( CGAlgorithmsDD, staticAccessors$6 );
94457
94458         var CoordinateSequence = function CoordinateSequence () {};
94459
94460         var staticAccessors$8 = { X: { configurable: true },Y: { configurable: true },Z: { configurable: true },M: { configurable: true } };
94461
94462         staticAccessors$8.X.get = function () { return 0 };
94463         staticAccessors$8.Y.get = function () { return 1 };
94464         staticAccessors$8.Z.get = function () { return 2 };
94465         staticAccessors$8.M.get = function () { return 3 };
94466         CoordinateSequence.prototype.setOrdinate = function setOrdinate (index, ordinateIndex, value) {};
94467         CoordinateSequence.prototype.size = function size () {};
94468         CoordinateSequence.prototype.getOrdinate = function getOrdinate (index, ordinateIndex) {};
94469         CoordinateSequence.prototype.getCoordinate = function getCoordinate () {};
94470         CoordinateSequence.prototype.getCoordinateCopy = function getCoordinateCopy (i) {};
94471         CoordinateSequence.prototype.getDimension = function getDimension () {};
94472         CoordinateSequence.prototype.getX = function getX (index) {};
94473         CoordinateSequence.prototype.clone = function clone () {};
94474         CoordinateSequence.prototype.expandEnvelope = function expandEnvelope (env) {};
94475         CoordinateSequence.prototype.copy = function copy () {};
94476         CoordinateSequence.prototype.getY = function getY (index) {};
94477         CoordinateSequence.prototype.toCoordinateArray = function toCoordinateArray () {};
94478         CoordinateSequence.prototype.interfaces_ = function interfaces_ () {
94479           return [Clonable]
94480         };
94481         CoordinateSequence.prototype.getClass = function getClass () {
94482           return CoordinateSequence
94483         };
94484
94485         Object.defineProperties( CoordinateSequence, staticAccessors$8 );
94486
94487         var Exception = function Exception () {};
94488
94489         var NotRepresentableException = (function (Exception$$1) {
94490           function NotRepresentableException () {
94491             Exception$$1.call(this, 'Projective point not representable on the Cartesian plane.');
94492           }
94493
94494           if ( Exception$$1 ) { NotRepresentableException.__proto__ = Exception$$1; }
94495           NotRepresentableException.prototype = Object.create( Exception$$1 && Exception$$1.prototype );
94496           NotRepresentableException.prototype.constructor = NotRepresentableException;
94497           NotRepresentableException.prototype.interfaces_ = function interfaces_ () {
94498             return []
94499           };
94500           NotRepresentableException.prototype.getClass = function getClass () {
94501             return NotRepresentableException
94502           };
94503
94504           return NotRepresentableException;
94505         }(Exception));
94506
94507         var System = function System () {};
94508
94509         System.arraycopy = function arraycopy (src, srcPos, dest, destPos, len) {
94510           var c = 0;
94511           for (var i = srcPos; i < srcPos + len; i++) {
94512             dest[destPos + c] = src[i];
94513             c++;
94514           }
94515         };
94516
94517         System.getProperty = function getProperty (name) {
94518           return {
94519             'line.separator': '\n'
94520           }[name]
94521         };
94522
94523         var HCoordinate = function HCoordinate () {
94524           this.x = null;
94525           this.y = null;
94526           this.w = null;
94527           if (arguments.length === 0) {
94528             this.x = 0.0;
94529             this.y = 0.0;
94530             this.w = 1.0;
94531           } else if (arguments.length === 1) {
94532             var p = arguments[0];
94533             this.x = p.x;
94534             this.y = p.y;
94535             this.w = 1.0;
94536           } else if (arguments.length === 2) {
94537             if (typeof arguments[0] === 'number' && typeof arguments[1] === 'number') {
94538               var _x = arguments[0];
94539               var _y = arguments[1];
94540               this.x = _x;
94541               this.y = _y;
94542               this.w = 1.0;
94543             } else if (arguments[0] instanceof HCoordinate && arguments[1] instanceof HCoordinate) {
94544               var p1 = arguments[0];
94545               var p2 = arguments[1];
94546               this.x = p1.y * p2.w - p2.y * p1.w;
94547               this.y = p2.x * p1.w - p1.x * p2.w;
94548               this.w = p1.x * p2.y - p2.x * p1.y;
94549             } else if (arguments[0] instanceof Coordinate && arguments[1] instanceof Coordinate) {
94550               var p1$1 = arguments[0];
94551               var p2$1 = arguments[1];
94552               this.x = p1$1.y - p2$1.y;
94553               this.y = p2$1.x - p1$1.x;
94554               this.w = p1$1.x * p2$1.y - p2$1.x * p1$1.y;
94555             }
94556           } else if (arguments.length === 3) {
94557             var _x$1 = arguments[0];
94558             var _y$1 = arguments[1];
94559             var _w = arguments[2];
94560             this.x = _x$1;
94561             this.y = _y$1;
94562             this.w = _w;
94563           } else if (arguments.length === 4) {
94564             var p1$2 = arguments[0];
94565             var p2$2 = arguments[1];
94566             var q1 = arguments[2];
94567             var q2 = arguments[3];
94568             var px = p1$2.y - p2$2.y;
94569             var py = p2$2.x - p1$2.x;
94570             var pw = p1$2.x * p2$2.y - p2$2.x * p1$2.y;
94571             var qx = q1.y - q2.y;
94572             var qy = q2.x - q1.x;
94573             var qw = q1.x * q2.y - q2.x * q1.y;
94574             this.x = py * qw - qy * pw;
94575             this.y = qx * pw - px * qw;
94576             this.w = px * qy - qx * py;
94577           }
94578         };
94579         HCoordinate.prototype.getY = function getY () {
94580           var a = this.y / this.w;
94581           if (Double.isNaN(a) || Double.isInfinite(a)) {
94582             throw new NotRepresentableException()
94583           }
94584           return a
94585         };
94586         HCoordinate.prototype.getX = function getX () {
94587           var a = this.x / this.w;
94588           if (Double.isNaN(a) || Double.isInfinite(a)) {
94589             throw new NotRepresentableException()
94590           }
94591           return a
94592         };
94593         HCoordinate.prototype.getCoordinate = function getCoordinate () {
94594           var p = new Coordinate();
94595           p.x = this.getX();
94596           p.y = this.getY();
94597           return p
94598         };
94599         HCoordinate.prototype.interfaces_ = function interfaces_ () {
94600           return []
94601         };
94602         HCoordinate.prototype.getClass = function getClass () {
94603           return HCoordinate
94604         };
94605         HCoordinate.intersection = function intersection (p1, p2, q1, q2) {
94606           var px = p1.y - p2.y;
94607           var py = p2.x - p1.x;
94608           var pw = p1.x * p2.y - p2.x * p1.y;
94609           var qx = q1.y - q2.y;
94610           var qy = q2.x - q1.x;
94611           var qw = q1.x * q2.y - q2.x * q1.y;
94612           var x = py * qw - qy * pw;
94613           var y = qx * pw - px * qw;
94614           var w = px * qy - qx * py;
94615           var xInt = x / w;
94616           var yInt = y / w;
94617           if (Double.isNaN(xInt) || (Double.isInfinite(xInt) || Double.isNaN(yInt)) || Double.isInfinite(yInt)) {
94618             throw new NotRepresentableException()
94619           }
94620           return new Coordinate(xInt, yInt)
94621         };
94622
94623         var Envelope = function Envelope () {
94624           this._minx = null;
94625           this._maxx = null;
94626           this._miny = null;
94627           this._maxy = null;
94628           if (arguments.length === 0) {
94629             this.init();
94630           } else if (arguments.length === 1) {
94631             if (arguments[0] instanceof Coordinate) {
94632               var p = arguments[0];
94633               this.init(p.x, p.x, p.y, p.y);
94634             } else if (arguments[0] instanceof Envelope) {
94635               var env = arguments[0];
94636               this.init(env);
94637             }
94638           } else if (arguments.length === 2) {
94639             var p1 = arguments[0];
94640             var p2 = arguments[1];
94641             this.init(p1.x, p2.x, p1.y, p2.y);
94642           } else if (arguments.length === 4) {
94643             var x1 = arguments[0];
94644             var x2 = arguments[1];
94645             var y1 = arguments[2];
94646             var y2 = arguments[3];
94647             this.init(x1, x2, y1, y2);
94648           }
94649         };
94650
94651         var staticAccessors$9 = { serialVersionUID: { configurable: true } };
94652         Envelope.prototype.getArea = function getArea () {
94653           return this.getWidth() * this.getHeight()
94654         };
94655         Envelope.prototype.equals = function equals (other) {
94656           if (!(other instanceof Envelope)) {
94657             return false
94658           }
94659           var otherEnvelope = other;
94660           if (this.isNull()) {
94661             return otherEnvelope.isNull()
94662           }
94663           return this._maxx === otherEnvelope.getMaxX() && this._maxy === otherEnvelope.getMaxY() && this._minx === otherEnvelope.getMinX() && this._miny === otherEnvelope.getMinY()
94664         };
94665         Envelope.prototype.intersection = function intersection (env) {
94666           if (this.isNull() || env.isNull() || !this.intersects(env)) { return new Envelope() }
94667           var intMinX = this._minx > env._minx ? this._minx : env._minx;
94668           var intMinY = this._miny > env._miny ? this._miny : env._miny;
94669           var intMaxX = this._maxx < env._maxx ? this._maxx : env._maxx;
94670           var intMaxY = this._maxy < env._maxy ? this._maxy : env._maxy;
94671           return new Envelope(intMinX, intMaxX, intMinY, intMaxY)
94672         };
94673         Envelope.prototype.isNull = function isNull () {
94674           return this._maxx < this._minx
94675         };
94676         Envelope.prototype.getMaxX = function getMaxX () {
94677           return this._maxx
94678         };
94679         Envelope.prototype.covers = function covers () {
94680           if (arguments.length === 1) {
94681             if (arguments[0] instanceof Coordinate) {
94682               var p = arguments[0];
94683               return this.covers(p.x, p.y)
94684             } else if (arguments[0] instanceof Envelope) {
94685               var other = arguments[0];
94686               if (this.isNull() || other.isNull()) {
94687                 return false
94688               }
94689               return other.getMinX() >= this._minx && other.getMaxX() <= this._maxx && other.getMinY() >= this._miny && other.getMaxY() <= this._maxy
94690             }
94691           } else if (arguments.length === 2) {
94692             var x = arguments[0];
94693             var y = arguments[1];
94694             if (this.isNull()) { return false }
94695             return x >= this._minx && x <= this._maxx && y >= this._miny && y <= this._maxy
94696           }
94697         };
94698         Envelope.prototype.intersects = function intersects () {
94699           if (arguments.length === 1) {
94700             if (arguments[0] instanceof Envelope) {
94701               var other = arguments[0];
94702               if (this.isNull() || other.isNull()) {
94703                 return false
94704               }
94705               return !(other._minx > this._maxx || other._maxx < this._minx || other._miny > this._maxy || other._maxy < this._miny)
94706             } else if (arguments[0] instanceof Coordinate) {
94707               var p = arguments[0];
94708               return this.intersects(p.x, p.y)
94709             }
94710           } else if (arguments.length === 2) {
94711             var x = arguments[0];
94712             var y = arguments[1];
94713             if (this.isNull()) { return false }
94714             return !(x > this._maxx || x < this._minx || y > this._maxy || y < this._miny)
94715           }
94716         };
94717         Envelope.prototype.getMinY = function getMinY () {
94718           return this._miny
94719         };
94720         Envelope.prototype.getMinX = function getMinX () {
94721           return this._minx
94722         };
94723         Envelope.prototype.expandToInclude = function expandToInclude () {
94724           if (arguments.length === 1) {
94725             if (arguments[0] instanceof Coordinate) {
94726               var p = arguments[0];
94727               this.expandToInclude(p.x, p.y);
94728             } else if (arguments[0] instanceof Envelope) {
94729               var other = arguments[0];
94730               if (other.isNull()) {
94731                 return null
94732               }
94733               if (this.isNull()) {
94734                 this._minx = other.getMinX();
94735                 this._maxx = other.getMaxX();
94736                 this._miny = other.getMinY();
94737                 this._maxy = other.getMaxY();
94738               } else {
94739                 if (other._minx < this._minx) {
94740                   this._minx = other._minx;
94741                 }
94742                 if (other._maxx > this._maxx) {
94743                   this._maxx = other._maxx;
94744                 }
94745                 if (other._miny < this._miny) {
94746                   this._miny = other._miny;
94747                 }
94748                 if (other._maxy > this._maxy) {
94749                   this._maxy = other._maxy;
94750                 }
94751               }
94752             }
94753           } else if (arguments.length === 2) {
94754             var x = arguments[0];
94755             var y = arguments[1];
94756             if (this.isNull()) {
94757               this._minx = x;
94758               this._maxx = x;
94759               this._miny = y;
94760               this._maxy = y;
94761             } else {
94762               if (x < this._minx) {
94763                 this._minx = x;
94764               }
94765               if (x > this._maxx) {
94766                 this._maxx = x;
94767               }
94768               if (y < this._miny) {
94769                 this._miny = y;
94770               }
94771               if (y > this._maxy) {
94772                 this._maxy = y;
94773               }
94774             }
94775           }
94776         };
94777         Envelope.prototype.minExtent = function minExtent () {
94778           if (this.isNull()) { return 0.0 }
94779           var w = this.getWidth();
94780           var h = this.getHeight();
94781           if (w < h) { return w }
94782           return h
94783         };
94784         Envelope.prototype.getWidth = function getWidth () {
94785           if (this.isNull()) {
94786             return 0
94787           }
94788           return this._maxx - this._minx
94789         };
94790         Envelope.prototype.compareTo = function compareTo (o) {
94791           var env = o;
94792           if (this.isNull()) {
94793             if (env.isNull()) { return 0 }
94794             return -1
94795           } else {
94796             if (env.isNull()) { return 1 }
94797           }
94798           if (this._minx < env._minx) { return -1 }
94799           if (this._minx > env._minx) { return 1 }
94800           if (this._miny < env._miny) { return -1 }
94801           if (this._miny > env._miny) { return 1 }
94802           if (this._maxx < env._maxx) { return -1 }
94803           if (this._maxx > env._maxx) { return 1 }
94804           if (this._maxy < env._maxy) { return -1 }
94805           if (this._maxy > env._maxy) { return 1 }
94806           return 0
94807         };
94808         Envelope.prototype.translate = function translate (transX, transY) {
94809           if (this.isNull()) {
94810             return null
94811           }
94812           this.init(this.getMinX() + transX, this.getMaxX() + transX, this.getMinY() + transY, this.getMaxY() + transY);
94813         };
94814         Envelope.prototype.toString = function toString () {
94815           return 'Env[' + this._minx + ' : ' + this._maxx + ', ' + this._miny + ' : ' + this._maxy + ']'
94816         };
94817         Envelope.prototype.setToNull = function setToNull () {
94818           this._minx = 0;
94819           this._maxx = -1;
94820           this._miny = 0;
94821           this._maxy = -1;
94822         };
94823         Envelope.prototype.getHeight = function getHeight () {
94824           if (this.isNull()) {
94825             return 0
94826           }
94827           return this._maxy - this._miny
94828         };
94829         Envelope.prototype.maxExtent = function maxExtent () {
94830           if (this.isNull()) { return 0.0 }
94831           var w = this.getWidth();
94832           var h = this.getHeight();
94833           if (w > h) { return w }
94834           return h
94835         };
94836         Envelope.prototype.expandBy = function expandBy () {
94837           if (arguments.length === 1) {
94838             var distance = arguments[0];
94839             this.expandBy(distance, distance);
94840           } else if (arguments.length === 2) {
94841             var deltaX = arguments[0];
94842             var deltaY = arguments[1];
94843             if (this.isNull()) { return null }
94844             this._minx -= deltaX;
94845             this._maxx += deltaX;
94846             this._miny -= deltaY;
94847             this._maxy += deltaY;
94848             if (this._minx > this._maxx || this._miny > this._maxy) { this.setToNull(); }
94849           }
94850         };
94851         Envelope.prototype.contains = function contains () {
94852           if (arguments.length === 1) {
94853             if (arguments[0] instanceof Envelope) {
94854               var other = arguments[0];
94855               return this.covers(other)
94856             } else if (arguments[0] instanceof Coordinate) {
94857               var p = arguments[0];
94858               return this.covers(p)
94859             }
94860           } else if (arguments.length === 2) {
94861             var x = arguments[0];
94862             var y = arguments[1];
94863             return this.covers(x, y)
94864           }
94865         };
94866         Envelope.prototype.centre = function centre () {
94867           if (this.isNull()) { return null }
94868           return new Coordinate((this.getMinX() + this.getMaxX()) / 2.0, (this.getMinY() + this.getMaxY()) / 2.0)
94869         };
94870         Envelope.prototype.init = function init () {
94871           if (arguments.length === 0) {
94872             this.setToNull();
94873           } else if (arguments.length === 1) {
94874             if (arguments[0] instanceof Coordinate) {
94875               var p = arguments[0];
94876               this.init(p.x, p.x, p.y, p.y);
94877             } else if (arguments[0] instanceof Envelope) {
94878               var env = arguments[0];
94879               this._minx = env._minx;
94880               this._maxx = env._maxx;
94881               this._miny = env._miny;
94882               this._maxy = env._maxy;
94883             }
94884           } else if (arguments.length === 2) {
94885             var p1 = arguments[0];
94886             var p2 = arguments[1];
94887             this.init(p1.x, p2.x, p1.y, p2.y);
94888           } else if (arguments.length === 4) {
94889             var x1 = arguments[0];
94890             var x2 = arguments[1];
94891             var y1 = arguments[2];
94892             var y2 = arguments[3];
94893             if (x1 < x2) {
94894               this._minx = x1;
94895               this._maxx = x2;
94896             } else {
94897               this._minx = x2;
94898               this._maxx = x1;
94899             }
94900             if (y1 < y2) {
94901               this._miny = y1;
94902               this._maxy = y2;
94903             } else {
94904               this._miny = y2;
94905               this._maxy = y1;
94906             }
94907           }
94908         };
94909         Envelope.prototype.getMaxY = function getMaxY () {
94910           return this._maxy
94911         };
94912         Envelope.prototype.distance = function distance (env) {
94913           if (this.intersects(env)) { return 0 }
94914           var dx = 0.0;
94915           if (this._maxx < env._minx) { dx = env._minx - this._maxx; } else if (this._minx > env._maxx) { dx = this._minx - env._maxx; }
94916           var dy = 0.0;
94917           if (this._maxy < env._miny) { dy = env._miny - this._maxy; } else if (this._miny > env._maxy) { dy = this._miny - env._maxy; }
94918           if (dx === 0.0) { return dy }
94919           if (dy === 0.0) { return dx }
94920           return Math.sqrt(dx * dx + dy * dy)
94921         };
94922         Envelope.prototype.hashCode = function hashCode () {
94923           var result = 17;
94924           result = 37 * result + Coordinate.hashCode(this._minx);
94925           result = 37 * result + Coordinate.hashCode(this._maxx);
94926           result = 37 * result + Coordinate.hashCode(this._miny);
94927           result = 37 * result + Coordinate.hashCode(this._maxy);
94928           return result
94929         };
94930         Envelope.prototype.interfaces_ = function interfaces_ () {
94931           return [Comparable, Serializable]
94932         };
94933         Envelope.prototype.getClass = function getClass () {
94934           return Envelope
94935         };
94936         Envelope.intersects = function intersects () {
94937           if (arguments.length === 3) {
94938             var p1 = arguments[0];
94939             var p2 = arguments[1];
94940             var q = arguments[2];
94941             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))) {
94942               return true
94943             }
94944             return false
94945           } else if (arguments.length === 4) {
94946             var p1$1 = arguments[0];
94947             var p2$1 = arguments[1];
94948             var q1 = arguments[2];
94949             var q2 = arguments[3];
94950             var minq = Math.min(q1.x, q2.x);
94951             var maxq = Math.max(q1.x, q2.x);
94952             var minp = Math.min(p1$1.x, p2$1.x);
94953             var maxp = Math.max(p1$1.x, p2$1.x);
94954             if (minp > maxq) { return false }
94955             if (maxp < minq) { return false }
94956             minq = Math.min(q1.y, q2.y);
94957             maxq = Math.max(q1.y, q2.y);
94958             minp = Math.min(p1$1.y, p2$1.y);
94959             maxp = Math.max(p1$1.y, p2$1.y);
94960             if (minp > maxq) { return false }
94961             if (maxp < minq) { return false }
94962             return true
94963           }
94964         };
94965         staticAccessors$9.serialVersionUID.get = function () { return 5873921885273102420 };
94966
94967         Object.defineProperties( Envelope, staticAccessors$9 );
94968
94969         var regExes = {
94970           'typeStr': /^\s*(\w+)\s*\(\s*(.*)\s*\)\s*$/,
94971           'emptyTypeStr': /^\s*(\w+)\s*EMPTY\s*$/,
94972           'spaces': /\s+/,
94973           'parenComma': /\)\s*,\s*\(/,
94974           'doubleParenComma': /\)\s*\)\s*,\s*\(\s*\(/, // can't use {2} here
94975           'trimParens': /^\s*\(?(.*?)\)?\s*$/
94976         };
94977
94978         /**
94979          * Class for reading and writing Well-Known Text.
94980          *
94981          * NOTE: Adapted from OpenLayers 2.11 implementation.
94982          */
94983
94984         /** Create a new parser for WKT
94985          *
94986          * @param {GeometryFactory} geometryFactory
94987          * @return An instance of WKTParser.
94988          * @constructor
94989          * @private
94990          */
94991         var WKTParser = function WKTParser (geometryFactory) {
94992           this.geometryFactory = geometryFactory || new GeometryFactory();
94993         };
94994         /**
94995          * Deserialize a WKT string and return a geometry. Supports WKT for POINT,
94996          * MULTIPOINT, LINESTRING, LINEARRING, MULTILINESTRING, POLYGON, MULTIPOLYGON,
94997          * and GEOMETRYCOLLECTION.
94998          *
94999          * @param {String} wkt A WKT string.
95000          * @return {Geometry} A geometry instance.
95001          * @private
95002          */
95003         WKTParser.prototype.read = function read (wkt) {
95004           var geometry, type, str;
95005           wkt = wkt.replace(/[\n\r]/g, ' ');
95006           var matches = regExes.typeStr.exec(wkt);
95007           if (wkt.search('EMPTY') !== -1) {
95008             matches = regExes.emptyTypeStr.exec(wkt);
95009             matches[2] = undefined;
95010           }
95011           if (matches) {
95012             type = matches[1].toLowerCase();
95013             str = matches[2];
95014             if (parse$1[type]) {
95015               geometry = parse$1[type].apply(this, [str]);
95016             }
95017           }
95018
95019           if (geometry === undefined) { throw new Error('Could not parse WKT ' + wkt) }
95020
95021           return geometry
95022         };
95023
95024         /**
95025          * Serialize a geometry into a WKT string.
95026          *
95027          * @param {Geometry} geometry A feature or array of features.
95028          * @return {String} The WKT string representation of the input geometries.
95029          * @private
95030          */
95031         WKTParser.prototype.write = function write (geometry) {
95032           return this.extractGeometry(geometry)
95033         };
95034
95035         /**
95036          * Entry point to construct the WKT for a single Geometry object.
95037          *
95038          * @param {Geometry} geometry
95039          * @return {String} A WKT string of representing the geometry.
95040          * @private
95041          */
95042         WKTParser.prototype.extractGeometry = function extractGeometry (geometry) {
95043           var type = geometry.getGeometryType().toLowerCase();
95044           if (!extract$1[type]) {
95045             return null
95046           }
95047           var wktType = type.toUpperCase();
95048           var data;
95049           if (geometry.isEmpty()) {
95050             data = wktType + ' EMPTY';
95051           } else {
95052             data = wktType + '(' + extract$1[type].apply(this, [geometry]) + ')';
95053           }
95054           return data
95055         };
95056
95057         /**
95058          * Object with properties corresponding to the geometry types. Property values
95059          * are functions that do the actual data extraction.
95060          * @private
95061          */
95062         var extract$1 = {
95063           coordinate: function coordinate (coordinate$1) {
95064             return coordinate$1.x + ' ' + coordinate$1.y
95065           },
95066
95067           /**
95068            * Return a space delimited string of point coordinates.
95069            *
95070            * @param {Point}
95071            *          point
95072            * @return {String} A string of coordinates representing the point.
95073            */
95074           point: function point (point$1) {
95075             return extract$1.coordinate.call(this, point$1._coordinates._coordinates[0])
95076           },
95077
95078           /**
95079            * Return a comma delimited string of point coordinates from a multipoint.
95080            *
95081            * @param {MultiPoint}
95082            *          multipoint
95083            * @return {String} A string of point coordinate strings representing the
95084            *         multipoint.
95085            */
95086           multipoint: function multipoint (multipoint$1) {
95087             var this$1 = this;
95088
95089             var array = [];
95090             for (var i = 0, len = multipoint$1._geometries.length; i < len; ++i) {
95091               array.push('(' + extract$1.point.apply(this$1, [multipoint$1._geometries[i]]) + ')');
95092             }
95093             return array.join(',')
95094           },
95095
95096           /**
95097            * Return a comma delimited string of point coordinates from a line.
95098            *
95099            * @param {LineString} linestring
95100            * @return {String} A string of point coordinate strings representing the linestring.
95101            */
95102           linestring: function linestring (linestring$1) {
95103             var this$1 = this;
95104
95105             var array = [];
95106             for (var i = 0, len = linestring$1._points._coordinates.length; i < len; ++i) {
95107               array.push(extract$1.coordinate.apply(this$1, [linestring$1._points._coordinates[i]]));
95108             }
95109             return array.join(',')
95110           },
95111
95112           linearring: function linearring (linearring$1) {
95113             var this$1 = this;
95114
95115             var array = [];
95116             for (var i = 0, len = linearring$1._points._coordinates.length; i < len; ++i) {
95117               array.push(extract$1.coordinate.apply(this$1, [linearring$1._points._coordinates[i]]));
95118             }
95119             return array.join(',')
95120           },
95121
95122           /**
95123            * Return a comma delimited string of linestring strings from a
95124            * multilinestring.
95125            *
95126            * @param {MultiLineString} multilinestring
95127            * @return {String} A string of of linestring strings representing the multilinestring.
95128            */
95129           multilinestring: function multilinestring (multilinestring$1) {
95130             var this$1 = this;
95131
95132             var array = [];
95133             for (var i = 0, len = multilinestring$1._geometries.length; i < len; ++i) {
95134               array.push('(' +
95135                 extract$1.linestring.apply(this$1, [multilinestring$1._geometries[i]]) +
95136                 ')');
95137             }
95138             return array.join(',')
95139           },
95140
95141           /**
95142            * Return a comma delimited string of linear ring arrays from a polygon.
95143            *
95144            * @param {Polygon} polygon
95145            * @return {String} An array of linear ring arrays representing the polygon.
95146            */
95147           polygon: function polygon (polygon$1) {
95148             var this$1 = this;
95149
95150             var array = [];
95151             array.push('(' + extract$1.linestring.apply(this, [polygon$1._shell]) + ')');
95152             for (var i = 0, len = polygon$1._holes.length; i < len; ++i) {
95153               array.push('(' + extract$1.linestring.apply(this$1, [polygon$1._holes[i]]) + ')');
95154             }
95155             return array.join(',')
95156           },
95157
95158           /**
95159            * Return an array of polygon arrays from a multipolygon.
95160            *
95161            * @param {MultiPolygon} multipolygon
95162            * @return {String} An array of polygon arrays representing the multipolygon.
95163            */
95164           multipolygon: function multipolygon (multipolygon$1) {
95165             var this$1 = this;
95166
95167             var array = [];
95168             for (var i = 0, len = multipolygon$1._geometries.length; i < len; ++i) {
95169               array.push('(' + extract$1.polygon.apply(this$1, [multipolygon$1._geometries[i]]) + ')');
95170             }
95171             return array.join(',')
95172           },
95173
95174           /**
95175            * Return the WKT portion between 'GEOMETRYCOLLECTION(' and ')' for an
95176            * geometrycollection.
95177            *
95178            * @param {GeometryCollection} collection
95179            * @return {String} internal WKT representation of the collection.
95180            */
95181           geometrycollection: function geometrycollection (collection) {
95182             var this$1 = this;
95183
95184             var array = [];
95185             for (var i = 0, len = collection._geometries.length; i < len; ++i) {
95186               array.push(this$1.extractGeometry(collection._geometries[i]));
95187             }
95188             return array.join(',')
95189           }
95190         };
95191
95192         /**
95193          * Object with properties corresponding to the geometry types. Property values
95194          * are functions that do the actual parsing.
95195          * @private
95196          */
95197         var parse$1 = {
95198           /**
95199            * Return point geometry given a point WKT fragment.
95200            *
95201            * @param {String} str A WKT fragment representing the point.
95202            * @return {Point} A point geometry.
95203            * @private
95204            */
95205           point: function point (str) {
95206             if (str === undefined) {
95207               return this.geometryFactory.createPoint()
95208             }
95209
95210             var coords = str.trim().split(regExes.spaces);
95211             return this.geometryFactory.createPoint(new Coordinate(Number.parseFloat(coords[0]),
95212               Number.parseFloat(coords[1])))
95213           },
95214
95215           /**
95216            * Return a multipoint geometry given a multipoint WKT fragment.
95217            *
95218            * @param {String} str A WKT fragment representing the multipoint.
95219            * @return {Point} A multipoint feature.
95220            * @private
95221            */
95222           multipoint: function multipoint (str) {
95223             var this$1 = this;
95224
95225             if (str === undefined) {
95226               return this.geometryFactory.createMultiPoint()
95227             }
95228
95229             var point;
95230             var points = str.trim().split(',');
95231             var components = [];
95232             for (var i = 0, len = points.length; i < len; ++i) {
95233               point = points[i].replace(regExes.trimParens, '$1');
95234               components.push(parse$1.point.apply(this$1, [point]));
95235             }
95236             return this.geometryFactory.createMultiPoint(components)
95237           },
95238
95239           /**
95240            * Return a linestring geometry given a linestring WKT fragment.
95241            *
95242            * @param {String} str A WKT fragment representing the linestring.
95243            * @return {LineString} A linestring geometry.
95244            * @private
95245            */
95246           linestring: function linestring (str) {
95247             if (str === undefined) {
95248               return this.geometryFactory.createLineString()
95249             }
95250
95251             var points = str.trim().split(',');
95252             var components = [];
95253             var coords;
95254             for (var i = 0, len = points.length; i < len; ++i) {
95255               coords = points[i].trim().split(regExes.spaces);
95256               components.push(new Coordinate(Number.parseFloat(coords[0]), Number.parseFloat(coords[1])));
95257             }
95258             return this.geometryFactory.createLineString(components)
95259           },
95260
95261           /**
95262            * Return a linearring geometry given a linearring WKT fragment.
95263            *
95264            * @param {String} str A WKT fragment representing the linearring.
95265            * @return {LinearRing} A linearring geometry.
95266            * @private
95267            */
95268           linearring: function linearring (str) {
95269             if (str === undefined) {
95270               return this.geometryFactory.createLinearRing()
95271             }
95272
95273             var points = str.trim().split(',');
95274             var components = [];
95275             var coords;
95276             for (var i = 0, len = points.length; i < len; ++i) {
95277               coords = points[i].trim().split(regExes.spaces);
95278               components.push(new Coordinate(Number.parseFloat(coords[0]), Number.parseFloat(coords[1])));
95279             }
95280             return this.geometryFactory.createLinearRing(components)
95281           },
95282
95283           /**
95284            * Return a multilinestring geometry given a multilinestring WKT fragment.
95285            *
95286            * @param {String} str A WKT fragment representing the multilinestring.
95287            * @return {MultiLineString} A multilinestring geometry.
95288            * @private
95289            */
95290           multilinestring: function multilinestring (str) {
95291             var this$1 = this;
95292
95293             if (str === undefined) {
95294               return this.geometryFactory.createMultiLineString()
95295             }
95296
95297             var line;
95298             var lines = str.trim().split(regExes.parenComma);
95299             var components = [];
95300             for (var i = 0, len = lines.length; i < len; ++i) {
95301               line = lines[i].replace(regExes.trimParens, '$1');
95302               components.push(parse$1.linestring.apply(this$1, [line]));
95303             }
95304             return this.geometryFactory.createMultiLineString(components)
95305           },
95306
95307           /**
95308            * Return a polygon geometry given a polygon WKT fragment.
95309            *
95310            * @param {String} str A WKT fragment representing the polygon.
95311            * @return {Polygon} A polygon geometry.
95312            * @private
95313            */
95314           polygon: function polygon (str) {
95315             var this$1 = this;
95316
95317             if (str === undefined) {
95318               return this.geometryFactory.createPolygon()
95319             }
95320
95321             var ring, linestring, linearring;
95322             var rings = str.trim().split(regExes.parenComma);
95323             var shell;
95324             var holes = [];
95325             for (var i = 0, len = rings.length; i < len; ++i) {
95326               ring = rings[i].replace(regExes.trimParens, '$1');
95327               linestring = parse$1.linestring.apply(this$1, [ring]);
95328               linearring = this$1.geometryFactory.createLinearRing(linestring._points);
95329               if (i === 0) {
95330                 shell = linearring;
95331               } else {
95332                 holes.push(linearring);
95333               }
95334             }
95335             return this.geometryFactory.createPolygon(shell, holes)
95336           },
95337
95338           /**
95339            * Return a multipolygon geometry given a multipolygon WKT fragment.
95340            *
95341            * @param {String} str A WKT fragment representing the multipolygon.
95342            * @return {MultiPolygon} A multipolygon geometry.
95343            * @private
95344            */
95345           multipolygon: function multipolygon (str) {
95346             var this$1 = this;
95347
95348             if (str === undefined) {
95349               return this.geometryFactory.createMultiPolygon()
95350             }
95351
95352             var polygon;
95353             var polygons = str.trim().split(regExes.doubleParenComma);
95354             var components = [];
95355             for (var i = 0, len = polygons.length; i < len; ++i) {
95356               polygon = polygons[i].replace(regExes.trimParens, '$1');
95357               components.push(parse$1.polygon.apply(this$1, [polygon]));
95358             }
95359             return this.geometryFactory.createMultiPolygon(components)
95360           },
95361
95362           /**
95363            * Return a geometrycollection given a geometrycollection WKT fragment.
95364            *
95365            * @param {String} str A WKT fragment representing the geometrycollection.
95366            * @return {GeometryCollection}
95367            * @private
95368            */
95369           geometrycollection: function geometrycollection (str) {
95370             var this$1 = this;
95371
95372             if (str === undefined) {
95373               return this.geometryFactory.createGeometryCollection()
95374             }
95375
95376             // separate components of the collection with |
95377             str = str.replace(/,\s*([A-Za-z])/g, '|$1');
95378             var wktArray = str.trim().split('|');
95379             var components = [];
95380             for (var i = 0, len = wktArray.length; i < len; ++i) {
95381               components.push(this$1.read(wktArray[i]));
95382             }
95383             return this.geometryFactory.createGeometryCollection(components)
95384           }
95385         };
95386
95387         /**
95388          * Writes the Well-Known Text representation of a {@link Geometry}. The
95389          * Well-Known Text format is defined in the <A
95390          * HREF="http://www.opengis.org/techno/specs.htm"> OGC Simple Features
95391          * Specification for SQL</A>.
95392          * <p>
95393          * The <code>WKTWriter</code> outputs coordinates rounded to the precision
95394          * model. Only the maximum number of decimal places necessary to represent the
95395          * ordinates to the required precision will be output.
95396          * <p>
95397          * The SFS WKT spec does not define a special tag for {@link LinearRing}s.
95398          * Under the spec, rings are output as <code>LINESTRING</code>s.
95399          */
95400
95401         /**
95402          * @param {GeometryFactory} geometryFactory
95403          * @constructor
95404          */
95405         var WKTWriter = function WKTWriter (geometryFactory) {
95406           this.parser = new WKTParser(geometryFactory);
95407         };
95408
95409         /**
95410          * Converts a <code>Geometry</code> to its Well-known Text representation.
95411          *
95412          * @param {Geometry} geometry a <code>Geometry</code> to process.
95413          * @return {string} a <Geometry Tagged Text> string (see the OpenGIS Simple
95414          *       Features Specification).
95415          * @memberof WKTWriter
95416          */
95417         WKTWriter.prototype.write = function write (geometry) {
95418           return this.parser.write(geometry)
95419         };
95420         /**
95421          * Generates the WKT for a <tt>LINESTRING</tt> specified by two
95422          * {@link Coordinate}s.
95423          *
95424          * @param p0 the first coordinate.
95425          * @param p1 the second coordinate.
95426          *
95427          * @return the WKT.
95428          * @private
95429          */
95430         WKTWriter.toLineString = function toLineString (p0, p1) {
95431           if (arguments.length !== 2) {
95432             throw new Error('Not implemented')
95433           }
95434           return 'LINESTRING ( ' + p0.x + ' ' + p0.y + ', ' + p1.x + ' ' + p1.y + ' )'
95435         };
95436
95437         var RuntimeException = (function (Error) {
95438           function RuntimeException (message) {
95439             Error.call(this, message);
95440             this.name = 'RuntimeException';
95441             this.message = message;
95442             this.stack = (new Error()).stack;
95443           }
95444
95445           if ( Error ) { RuntimeException.__proto__ = Error; }
95446           RuntimeException.prototype = Object.create( Error && Error.prototype );
95447           RuntimeException.prototype.constructor = RuntimeException;
95448
95449           return RuntimeException;
95450         }(Error));
95451
95452         var AssertionFailedException = (function (RuntimeException$$1) {
95453           function AssertionFailedException () {
95454             RuntimeException$$1.call(this);
95455             if (arguments.length === 0) {
95456               RuntimeException$$1.call(this);
95457             } else if (arguments.length === 1) {
95458               var message = arguments[0];
95459               RuntimeException$$1.call(this, message);
95460             }
95461           }
95462
95463           if ( RuntimeException$$1 ) { AssertionFailedException.__proto__ = RuntimeException$$1; }
95464           AssertionFailedException.prototype = Object.create( RuntimeException$$1 && RuntimeException$$1.prototype );
95465           AssertionFailedException.prototype.constructor = AssertionFailedException;
95466           AssertionFailedException.prototype.interfaces_ = function interfaces_ () {
95467             return []
95468           };
95469           AssertionFailedException.prototype.getClass = function getClass () {
95470             return AssertionFailedException
95471           };
95472
95473           return AssertionFailedException;
95474         }(RuntimeException));
95475
95476         var Assert = function Assert () {};
95477
95478         Assert.prototype.interfaces_ = function interfaces_ () {
95479           return []
95480         };
95481         Assert.prototype.getClass = function getClass () {
95482           return Assert
95483         };
95484         Assert.shouldNeverReachHere = function shouldNeverReachHere () {
95485           if (arguments.length === 0) {
95486             Assert.shouldNeverReachHere(null);
95487           } else if (arguments.length === 1) {
95488             var message = arguments[0];
95489             throw new AssertionFailedException('Should never reach here' + (message !== null ? ': ' + message : ''))
95490           }
95491         };
95492         Assert.isTrue = function isTrue () {
95493           var assertion;
95494           var message;
95495           if (arguments.length === 1) {
95496             assertion = arguments[0];
95497             Assert.isTrue(assertion, null);
95498           } else if (arguments.length === 2) {
95499             assertion = arguments[0];
95500             message = arguments[1];
95501             if (!assertion) {
95502               if (message === null) {
95503                 throw new AssertionFailedException()
95504               } else {
95505                 throw new AssertionFailedException(message)
95506               }
95507             }
95508           }
95509         };
95510         Assert.equals = function equals () {
95511           var expectedValue;
95512           var actualValue;
95513           var message;
95514           if (arguments.length === 2) {
95515             expectedValue = arguments[0];
95516             actualValue = arguments[1];
95517             Assert.equals(expectedValue, actualValue, null);
95518           } else if (arguments.length === 3) {
95519             expectedValue = arguments[0];
95520             actualValue = arguments[1];
95521             message = arguments[2];
95522             if (!actualValue.equals(expectedValue)) {
95523               throw new AssertionFailedException('Expected ' + expectedValue + ' but encountered ' + actualValue + (message !== null ? ': ' + message : ''))
95524             }
95525           }
95526         };
95527
95528         var LineIntersector = function LineIntersector () {
95529           this._result = null;
95530           this._inputLines = Array(2).fill().map(function () { return Array(2); });
95531           this._intPt = new Array(2).fill(null);
95532           this._intLineIndex = null;
95533           this._isProper = null;
95534           this._pa = null;
95535           this._pb = null;
95536           this._precisionModel = null;
95537           this._intPt[0] = new Coordinate();
95538           this._intPt[1] = new Coordinate();
95539           this._pa = this._intPt[0];
95540           this._pb = this._intPt[1];
95541           this._result = 0;
95542         };
95543
95544         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 } };
95545         LineIntersector.prototype.getIndexAlongSegment = function getIndexAlongSegment (segmentIndex, intIndex) {
95546           this.computeIntLineIndex();
95547           return this._intLineIndex[segmentIndex][intIndex]
95548         };
95549         LineIntersector.prototype.getTopologySummary = function getTopologySummary () {
95550           var catBuf = new StringBuffer();
95551           if (this.isEndPoint()) { catBuf.append(' endpoint'); }
95552           if (this._isProper) { catBuf.append(' proper'); }
95553           if (this.isCollinear()) { catBuf.append(' collinear'); }
95554           return catBuf.toString()
95555         };
95556         LineIntersector.prototype.computeIntersection = function computeIntersection (p1, p2, p3, p4) {
95557           this._inputLines[0][0] = p1;
95558           this._inputLines[0][1] = p2;
95559           this._inputLines[1][0] = p3;
95560           this._inputLines[1][1] = p4;
95561           this._result = this.computeIntersect(p1, p2, p3, p4);
95562         };
95563         LineIntersector.prototype.getIntersectionNum = function getIntersectionNum () {
95564           return this._result
95565         };
95566         LineIntersector.prototype.computeIntLineIndex = function computeIntLineIndex () {
95567           if (arguments.length === 0) {
95568             if (this._intLineIndex === null) {
95569               this._intLineIndex = Array(2).fill().map(function () { return Array(2); });
95570               this.computeIntLineIndex(0);
95571               this.computeIntLineIndex(1);
95572             }
95573           } else if (arguments.length === 1) {
95574             var segmentIndex = arguments[0];
95575             var dist0 = this.getEdgeDistance(segmentIndex, 0);
95576             var dist1 = this.getEdgeDistance(segmentIndex, 1);
95577             if (dist0 > dist1) {
95578               this._intLineIndex[segmentIndex][0] = 0;
95579               this._intLineIndex[segmentIndex][1] = 1;
95580             } else {
95581               this._intLineIndex[segmentIndex][0] = 1;
95582               this._intLineIndex[segmentIndex][1] = 0;
95583             }
95584           }
95585         };
95586         LineIntersector.prototype.isProper = function isProper () {
95587           return this.hasIntersection() && this._isProper
95588         };
95589         LineIntersector.prototype.setPrecisionModel = function setPrecisionModel (precisionModel) {
95590           this._precisionModel = precisionModel;
95591         };
95592         LineIntersector.prototype.isInteriorIntersection = function isInteriorIntersection () {
95593             var this$1 = this;
95594
95595           if (arguments.length === 0) {
95596             if (this.isInteriorIntersection(0)) { return true }
95597             if (this.isInteriorIntersection(1)) { return true }
95598             return false
95599           } else if (arguments.length === 1) {
95600             var inputLineIndex = arguments[0];
95601             for (var i = 0; i < this._result; i++) {
95602               if (!(this$1._intPt[i].equals2D(this$1._inputLines[inputLineIndex][0]) || this$1._intPt[i].equals2D(this$1._inputLines[inputLineIndex][1]))) {
95603                 return true
95604               }
95605             }
95606             return false
95607           }
95608         };
95609         LineIntersector.prototype.getIntersection = function getIntersection (intIndex) {
95610           return this._intPt[intIndex]
95611         };
95612         LineIntersector.prototype.isEndPoint = function isEndPoint () {
95613           return this.hasIntersection() && !this._isProper
95614         };
95615         LineIntersector.prototype.hasIntersection = function hasIntersection () {
95616           return this._result !== LineIntersector.NO_INTERSECTION
95617         };
95618         LineIntersector.prototype.getEdgeDistance = function getEdgeDistance (segmentIndex, intIndex) {
95619           var dist = LineIntersector.computeEdgeDistance(this._intPt[intIndex], this._inputLines[segmentIndex][0], this._inputLines[segmentIndex][1]);
95620           return dist
95621         };
95622         LineIntersector.prototype.isCollinear = function isCollinear () {
95623           return this._result === LineIntersector.COLLINEAR_INTERSECTION
95624         };
95625         LineIntersector.prototype.toString = function toString () {
95626           return WKTWriter.toLineString(this._inputLines[0][0], this._inputLines[0][1]) + ' - ' + WKTWriter.toLineString(this._inputLines[1][0], this._inputLines[1][1]) + this.getTopologySummary()
95627         };
95628         LineIntersector.prototype.getEndpoint = function getEndpoint (segmentIndex, ptIndex) {
95629           return this._inputLines[segmentIndex][ptIndex]
95630         };
95631         LineIntersector.prototype.isIntersection = function isIntersection (pt) {
95632             var this$1 = this;
95633
95634           for (var i = 0; i < this._result; i++) {
95635             if (this$1._intPt[i].equals2D(pt)) {
95636               return true
95637             }
95638           }
95639           return false
95640         };
95641         LineIntersector.prototype.getIntersectionAlongSegment = function getIntersectionAlongSegment (segmentIndex, intIndex) {
95642           this.computeIntLineIndex();
95643           return this._intPt[this._intLineIndex[segmentIndex][intIndex]]
95644         };
95645         LineIntersector.prototype.interfaces_ = function interfaces_ () {
95646           return []
95647         };
95648         LineIntersector.prototype.getClass = function getClass () {
95649           return LineIntersector
95650         };
95651         LineIntersector.computeEdgeDistance = function computeEdgeDistance (p, p0, p1) {
95652           var dx = Math.abs(p1.x - p0.x);
95653           var dy = Math.abs(p1.y - p0.y);
95654           var dist = -1.0;
95655           if (p.equals(p0)) {
95656             dist = 0.0;
95657           } else if (p.equals(p1)) {
95658             if (dx > dy) { dist = dx; } else { dist = dy; }
95659           } else {
95660             var pdx = Math.abs(p.x - p0.x);
95661             var pdy = Math.abs(p.y - p0.y);
95662             if (dx > dy) { dist = pdx; } else { dist = pdy; }
95663             if (dist === 0.0 && !p.equals(p0)) {
95664               dist = Math.max(pdx, pdy);
95665             }
95666           }
95667           Assert.isTrue(!(dist === 0.0 && !p.equals(p0)), 'Bad distance calculation');
95668           return dist
95669         };
95670         LineIntersector.nonRobustComputeEdgeDistance = function nonRobustComputeEdgeDistance (p, p1, p2) {
95671           var dx = p.x - p1.x;
95672           var dy = p.y - p1.y;
95673           var dist = Math.sqrt(dx * dx + dy * dy);
95674           Assert.isTrue(!(dist === 0.0 && !p.equals(p1)), 'Invalid distance calculation');
95675           return dist
95676         };
95677         staticAccessors$10.DONT_INTERSECT.get = function () { return 0 };
95678         staticAccessors$10.DO_INTERSECT.get = function () { return 1 };
95679         staticAccessors$10.COLLINEAR.get = function () { return 2 };
95680         staticAccessors$10.NO_INTERSECTION.get = function () { return 0 };
95681         staticAccessors$10.POINT_INTERSECTION.get = function () { return 1 };
95682         staticAccessors$10.COLLINEAR_INTERSECTION.get = function () { return 2 };
95683
95684         Object.defineProperties( LineIntersector, staticAccessors$10 );
95685
95686         var RobustLineIntersector = (function (LineIntersector$$1) {
95687           function RobustLineIntersector () {
95688             LineIntersector$$1.apply(this, arguments);
95689           }
95690
95691           if ( LineIntersector$$1 ) { RobustLineIntersector.__proto__ = LineIntersector$$1; }
95692           RobustLineIntersector.prototype = Object.create( LineIntersector$$1 && LineIntersector$$1.prototype );
95693           RobustLineIntersector.prototype.constructor = RobustLineIntersector;
95694
95695           RobustLineIntersector.prototype.isInSegmentEnvelopes = function isInSegmentEnvelopes (intPt) {
95696             var env0 = new Envelope(this._inputLines[0][0], this._inputLines[0][1]);
95697             var env1 = new Envelope(this._inputLines[1][0], this._inputLines[1][1]);
95698             return env0.contains(intPt) && env1.contains(intPt)
95699           };
95700           RobustLineIntersector.prototype.computeIntersection = function computeIntersection () {
95701             if (arguments.length === 3) {
95702               var p = arguments[0];
95703               var p1 = arguments[1];
95704               var p2 = arguments[2];
95705               this._isProper = false;
95706               if (Envelope.intersects(p1, p2, p)) {
95707                 if (CGAlgorithms.orientationIndex(p1, p2, p) === 0 && CGAlgorithms.orientationIndex(p2, p1, p) === 0) {
95708                   this._isProper = true;
95709                   if (p.equals(p1) || p.equals(p2)) {
95710                     this._isProper = false;
95711                   }
95712                   this._result = LineIntersector$$1.POINT_INTERSECTION;
95713                   return null
95714                 }
95715               }
95716               this._result = LineIntersector$$1.NO_INTERSECTION;
95717             } else { return LineIntersector$$1.prototype.computeIntersection.apply(this, arguments) }
95718           };
95719           RobustLineIntersector.prototype.normalizeToMinimum = function normalizeToMinimum (n1, n2, n3, n4, normPt) {
95720             normPt.x = this.smallestInAbsValue(n1.x, n2.x, n3.x, n4.x);
95721             normPt.y = this.smallestInAbsValue(n1.y, n2.y, n3.y, n4.y);
95722             n1.x -= normPt.x;
95723             n1.y -= normPt.y;
95724             n2.x -= normPt.x;
95725             n2.y -= normPt.y;
95726             n3.x -= normPt.x;
95727             n3.y -= normPt.y;
95728             n4.x -= normPt.x;
95729             n4.y -= normPt.y;
95730           };
95731           RobustLineIntersector.prototype.safeHCoordinateIntersection = function safeHCoordinateIntersection (p1, p2, q1, q2) {
95732             var intPt = null;
95733             try {
95734               intPt = HCoordinate.intersection(p1, p2, q1, q2);
95735             } catch (e) {
95736               if (e instanceof NotRepresentableException) {
95737                 intPt = RobustLineIntersector.nearestEndpoint(p1, p2, q1, q2);
95738               } else { throw e }
95739             } finally {}
95740             return intPt
95741           };
95742           RobustLineIntersector.prototype.intersection = function intersection (p1, p2, q1, q2) {
95743             var intPt = this.intersectionWithNormalization(p1, p2, q1, q2);
95744             if (!this.isInSegmentEnvelopes(intPt)) {
95745               intPt = new Coordinate(RobustLineIntersector.nearestEndpoint(p1, p2, q1, q2));
95746             }
95747             if (this._precisionModel !== null) {
95748               this._precisionModel.makePrecise(intPt);
95749             }
95750             return intPt
95751           };
95752           RobustLineIntersector.prototype.smallestInAbsValue = function smallestInAbsValue (x1, x2, x3, x4) {
95753             var x = x1;
95754             var xabs = Math.abs(x);
95755             if (Math.abs(x2) < xabs) {
95756               x = x2;
95757               xabs = Math.abs(x2);
95758             }
95759             if (Math.abs(x3) < xabs) {
95760               x = x3;
95761               xabs = Math.abs(x3);
95762             }
95763             if (Math.abs(x4) < xabs) {
95764               x = x4;
95765             }
95766             return x
95767           };
95768           RobustLineIntersector.prototype.checkDD = function checkDD (p1, p2, q1, q2, intPt) {
95769             var intPtDD = CGAlgorithmsDD.intersection(p1, p2, q1, q2);
95770             var isIn = this.isInSegmentEnvelopes(intPtDD);
95771             System.out.println('DD in env = ' + isIn + '  --------------------- ' + intPtDD);
95772             if (intPt.distance(intPtDD) > 0.0001) {
95773               System.out.println('Distance = ' + intPt.distance(intPtDD));
95774             }
95775           };
95776           RobustLineIntersector.prototype.intersectionWithNormalization = function intersectionWithNormalization (p1, p2, q1, q2) {
95777             var n1 = new Coordinate(p1);
95778             var n2 = new Coordinate(p2);
95779             var n3 = new Coordinate(q1);
95780             var n4 = new Coordinate(q2);
95781             var normPt = new Coordinate();
95782             this.normalizeToEnvCentre(n1, n2, n3, n4, normPt);
95783             var intPt = this.safeHCoordinateIntersection(n1, n2, n3, n4);
95784             intPt.x += normPt.x;
95785             intPt.y += normPt.y;
95786             return intPt
95787           };
95788           RobustLineIntersector.prototype.computeCollinearIntersection = function computeCollinearIntersection (p1, p2, q1, q2) {
95789             var p1q1p2 = Envelope.intersects(p1, p2, q1);
95790             var p1q2p2 = Envelope.intersects(p1, p2, q2);
95791             var q1p1q2 = Envelope.intersects(q1, q2, p1);
95792             var q1p2q2 = Envelope.intersects(q1, q2, p2);
95793             if (p1q1p2 && p1q2p2) {
95794               this._intPt[0] = q1;
95795               this._intPt[1] = q2;
95796               return LineIntersector$$1.COLLINEAR_INTERSECTION
95797             }
95798             if (q1p1q2 && q1p2q2) {
95799               this._intPt[0] = p1;
95800               this._intPt[1] = p2;
95801               return LineIntersector$$1.COLLINEAR_INTERSECTION
95802             }
95803             if (p1q1p2 && q1p1q2) {
95804               this._intPt[0] = q1;
95805               this._intPt[1] = p1;
95806               return q1.equals(p1) && !p1q2p2 && !q1p2q2 ? LineIntersector$$1.POINT_INTERSECTION : LineIntersector$$1.COLLINEAR_INTERSECTION
95807             }
95808             if (p1q1p2 && q1p2q2) {
95809               this._intPt[0] = q1;
95810               this._intPt[1] = p2;
95811               return q1.equals(p2) && !p1q2p2 && !q1p1q2 ? LineIntersector$$1.POINT_INTERSECTION : LineIntersector$$1.COLLINEAR_INTERSECTION
95812             }
95813             if (p1q2p2 && q1p1q2) {
95814               this._intPt[0] = q2;
95815               this._intPt[1] = p1;
95816               return q2.equals(p1) && !p1q1p2 && !q1p2q2 ? LineIntersector$$1.POINT_INTERSECTION : LineIntersector$$1.COLLINEAR_INTERSECTION
95817             }
95818             if (p1q2p2 && q1p2q2) {
95819               this._intPt[0] = q2;
95820               this._intPt[1] = p2;
95821               return q2.equals(p2) && !p1q1p2 && !q1p1q2 ? LineIntersector$$1.POINT_INTERSECTION : LineIntersector$$1.COLLINEAR_INTERSECTION
95822             }
95823             return LineIntersector$$1.NO_INTERSECTION
95824           };
95825           RobustLineIntersector.prototype.normalizeToEnvCentre = function normalizeToEnvCentre (n00, n01, n10, n11, normPt) {
95826             var minX0 = n00.x < n01.x ? n00.x : n01.x;
95827             var minY0 = n00.y < n01.y ? n00.y : n01.y;
95828             var maxX0 = n00.x > n01.x ? n00.x : n01.x;
95829             var maxY0 = n00.y > n01.y ? n00.y : n01.y;
95830             var minX1 = n10.x < n11.x ? n10.x : n11.x;
95831             var minY1 = n10.y < n11.y ? n10.y : n11.y;
95832             var maxX1 = n10.x > n11.x ? n10.x : n11.x;
95833             var maxY1 = n10.y > n11.y ? n10.y : n11.y;
95834             var intMinX = minX0 > minX1 ? minX0 : minX1;
95835             var intMaxX = maxX0 < maxX1 ? maxX0 : maxX1;
95836             var intMinY = minY0 > minY1 ? minY0 : minY1;
95837             var intMaxY = maxY0 < maxY1 ? maxY0 : maxY1;
95838             var intMidX = (intMinX + intMaxX) / 2.0;
95839             var intMidY = (intMinY + intMaxY) / 2.0;
95840             normPt.x = intMidX;
95841             normPt.y = intMidY;
95842             n00.x -= normPt.x;
95843             n00.y -= normPt.y;
95844             n01.x -= normPt.x;
95845             n01.y -= normPt.y;
95846             n10.x -= normPt.x;
95847             n10.y -= normPt.y;
95848             n11.x -= normPt.x;
95849             n11.y -= normPt.y;
95850           };
95851           RobustLineIntersector.prototype.computeIntersect = function computeIntersect (p1, p2, q1, q2) {
95852             this._isProper = false;
95853             if (!Envelope.intersects(p1, p2, q1, q2)) { return LineIntersector$$1.NO_INTERSECTION }
95854             var Pq1 = CGAlgorithms.orientationIndex(p1, p2, q1);
95855             var Pq2 = CGAlgorithms.orientationIndex(p1, p2, q2);
95856             if ((Pq1 > 0 && Pq2 > 0) || (Pq1 < 0 && Pq2 < 0)) {
95857               return LineIntersector$$1.NO_INTERSECTION
95858             }
95859             var Qp1 = CGAlgorithms.orientationIndex(q1, q2, p1);
95860             var Qp2 = CGAlgorithms.orientationIndex(q1, q2, p2);
95861             if ((Qp1 > 0 && Qp2 > 0) || (Qp1 < 0 && Qp2 < 0)) {
95862               return LineIntersector$$1.NO_INTERSECTION
95863             }
95864             var collinear = Pq1 === 0 && Pq2 === 0 && Qp1 === 0 && Qp2 === 0;
95865             if (collinear) {
95866               return this.computeCollinearIntersection(p1, p2, q1, q2)
95867             }
95868             if (Pq1 === 0 || Pq2 === 0 || Qp1 === 0 || Qp2 === 0) {
95869               this._isProper = false;
95870               if (p1.equals2D(q1) || p1.equals2D(q2)) {
95871                 this._intPt[0] = p1;
95872               } else if (p2.equals2D(q1) || p2.equals2D(q2)) {
95873                 this._intPt[0] = p2;
95874               } else if (Pq1 === 0) {
95875                 this._intPt[0] = new Coordinate(q1);
95876               } else if (Pq2 === 0) {
95877                 this._intPt[0] = new Coordinate(q2);
95878               } else if (Qp1 === 0) {
95879                 this._intPt[0] = new Coordinate(p1);
95880               } else if (Qp2 === 0) {
95881                 this._intPt[0] = new Coordinate(p2);
95882               }
95883             } else {
95884               this._isProper = true;
95885               this._intPt[0] = this.intersection(p1, p2, q1, q2);
95886             }
95887             return LineIntersector$$1.POINT_INTERSECTION
95888           };
95889           RobustLineIntersector.prototype.interfaces_ = function interfaces_ () {
95890             return []
95891           };
95892           RobustLineIntersector.prototype.getClass = function getClass () {
95893             return RobustLineIntersector
95894           };
95895           RobustLineIntersector.nearestEndpoint = function nearestEndpoint (p1, p2, q1, q2) {
95896             var nearestPt = p1;
95897             var minDist = CGAlgorithms.distancePointLine(p1, q1, q2);
95898             var dist = CGAlgorithms.distancePointLine(p2, q1, q2);
95899             if (dist < minDist) {
95900               minDist = dist;
95901               nearestPt = p2;
95902             }
95903             dist = CGAlgorithms.distancePointLine(q1, p1, p2);
95904             if (dist < minDist) {
95905               minDist = dist;
95906               nearestPt = q1;
95907             }
95908             dist = CGAlgorithms.distancePointLine(q2, p1, p2);
95909             if (dist < minDist) {
95910               minDist = dist;
95911               nearestPt = q2;
95912             }
95913             return nearestPt
95914           };
95915
95916           return RobustLineIntersector;
95917         }(LineIntersector));
95918
95919         var RobustDeterminant = function RobustDeterminant () {};
95920
95921         RobustDeterminant.prototype.interfaces_ = function interfaces_ () {
95922           return []
95923         };
95924         RobustDeterminant.prototype.getClass = function getClass () {
95925           return RobustDeterminant
95926         };
95927         RobustDeterminant.orientationIndex = function orientationIndex (p1, p2, q) {
95928           var dx1 = p2.x - p1.x;
95929           var dy1 = p2.y - p1.y;
95930           var dx2 = q.x - p2.x;
95931           var dy2 = q.y - p2.y;
95932           return RobustDeterminant.signOfDet2x2(dx1, dy1, dx2, dy2)
95933         };
95934         RobustDeterminant.signOfDet2x2 = function signOfDet2x2 (x1, y1, x2, y2) {
95935           var sign = null;
95936           var swap = null;
95937           var k = null;
95938           sign = 1;
95939           if (x1 === 0.0 || y2 === 0.0) {
95940             if (y1 === 0.0 || x2 === 0.0) {
95941               return 0
95942             } else if (y1 > 0) {
95943               if (x2 > 0) {
95944                 return -sign
95945               } else {
95946                 return sign
95947               }
95948             } else {
95949               if (x2 > 0) {
95950                 return sign
95951               } else {
95952                 return -sign
95953               }
95954             }
95955           }
95956           if (y1 === 0.0 || x2 === 0.0) {
95957             if (y2 > 0) {
95958               if (x1 > 0) {
95959                 return sign
95960               } else {
95961                 return -sign
95962               }
95963             } else {
95964               if (x1 > 0) {
95965                 return -sign
95966               } else {
95967                 return sign
95968               }
95969             }
95970           }
95971           if (y1 > 0.0) {
95972             if (y2 > 0.0) {
95973               if (y1 <= y2) ; else {
95974                 sign = -sign;
95975                 swap = x1;
95976                 x1 = x2;
95977                 x2 = swap;
95978                 swap = y1;
95979                 y1 = y2;
95980                 y2 = swap;
95981               }
95982             } else {
95983               if (y1 <= -y2) {
95984                 sign = -sign;
95985                 x2 = -x2;
95986                 y2 = -y2;
95987               } else {
95988                 swap = x1;
95989                 x1 = -x2;
95990                 x2 = swap;
95991                 swap = y1;
95992                 y1 = -y2;
95993                 y2 = swap;
95994               }
95995             }
95996           } else {
95997             if (y2 > 0.0) {
95998               if (-y1 <= y2) {
95999                 sign = -sign;
96000                 x1 = -x1;
96001                 y1 = -y1;
96002               } else {
96003                 swap = -x1;
96004                 x1 = x2;
96005                 x2 = swap;
96006                 swap = -y1;
96007                 y1 = y2;
96008                 y2 = swap;
96009               }
96010             } else {
96011               if (y1 >= y2) {
96012                 x1 = -x1;
96013                 y1 = -y1;
96014                 x2 = -x2;
96015                 y2 = -y2;
96016               } else {
96017                 sign = -sign;
96018                 swap = -x1;
96019                 x1 = -x2;
96020                 x2 = swap;
96021                 swap = -y1;
96022                 y1 = -y2;
96023                 y2 = swap;
96024               }
96025             }
96026           }
96027           if (x1 > 0.0) {
96028             if (x2 > 0.0) {
96029               if (x1 <= x2) ; else {
96030                 return sign
96031               }
96032             } else {
96033               return sign
96034             }
96035           } else {
96036             if (x2 > 0.0) {
96037               return -sign
96038             } else {
96039               if (x1 >= x2) {
96040                 sign = -sign;
96041                 x1 = -x1;
96042                 x2 = -x2;
96043               } else {
96044                 return -sign
96045               }
96046             }
96047           }
96048           while (true) {
96049             k = Math.floor(x2 / x1);
96050             x2 = x2 - k * x1;
96051             y2 = y2 - k * y1;
96052             if (y2 < 0.0) {
96053               return -sign
96054             }
96055             if (y2 > y1) {
96056               return sign
96057             }
96058             if (x1 > x2 + x2) {
96059               if (y1 < y2 + y2) {
96060                 return sign
96061               }
96062             } else {
96063               if (y1 > y2 + y2) {
96064                 return -sign
96065               } else {
96066                 x2 = x1 - x2;
96067                 y2 = y1 - y2;
96068                 sign = -sign;
96069               }
96070             }
96071             if (y2 === 0.0) {
96072               if (x2 === 0.0) {
96073                 return 0
96074               } else {
96075                 return -sign
96076               }
96077             }
96078             if (x2 === 0.0) {
96079               return sign
96080             }
96081             k = Math.floor(x1 / x2);
96082             x1 = x1 - k * x2;
96083             y1 = y1 - k * y2;
96084             if (y1 < 0.0) {
96085               return sign
96086             }
96087             if (y1 > y2) {
96088               return -sign
96089             }
96090             if (x2 > x1 + x1) {
96091               if (y2 < y1 + y1) {
96092                 return -sign
96093               }
96094             } else {
96095               if (y2 > y1 + y1) {
96096                 return sign
96097               } else {
96098                 x1 = x2 - x1;
96099                 y1 = y2 - y1;
96100                 sign = -sign;
96101               }
96102             }
96103             if (y1 === 0.0) {
96104               if (x1 === 0.0) {
96105                 return 0
96106               } else {
96107                 return sign
96108               }
96109             }
96110             if (x1 === 0.0) {
96111               return -sign
96112             }
96113           }
96114         };
96115
96116         var RayCrossingCounter = function RayCrossingCounter () {
96117           this._p = null;
96118           this._crossingCount = 0;
96119           this._isPointOnSegment = false;
96120           var p = arguments[0];
96121           this._p = p;
96122         };
96123         RayCrossingCounter.prototype.countSegment = function countSegment (p1, p2) {
96124           if (p1.x < this._p.x && p2.x < this._p.x) { return null }
96125           if (this._p.x === p2.x && this._p.y === p2.y) {
96126             this._isPointOnSegment = true;
96127             return null
96128           }
96129           if (p1.y === this._p.y && p2.y === this._p.y) {
96130             var minx = p1.x;
96131             var maxx = p2.x;
96132             if (minx > maxx) {
96133               minx = p2.x;
96134               maxx = p1.x;
96135             }
96136             if (this._p.x >= minx && this._p.x <= maxx) {
96137               this._isPointOnSegment = true;
96138             }
96139             return null
96140           }
96141           if ((p1.y > this._p.y && p2.y <= this._p.y) || (p2.y > this._p.y && p1.y <= this._p.y)) {
96142             var x1 = p1.x - this._p.x;
96143             var y1 = p1.y - this._p.y;
96144             var x2 = p2.x - this._p.x;
96145             var y2 = p2.y - this._p.y;
96146             var xIntSign = RobustDeterminant.signOfDet2x2(x1, y1, x2, y2);
96147             if (xIntSign === 0.0) {
96148               this._isPointOnSegment = true;
96149               return null
96150             }
96151             if (y2 < y1) { xIntSign = -xIntSign; }
96152             if (xIntSign > 0.0) {
96153               this._crossingCount++;
96154             }
96155           }
96156         };
96157         RayCrossingCounter.prototype.isPointInPolygon = function isPointInPolygon () {
96158           return this.getLocation() !== Location.EXTERIOR
96159         };
96160         RayCrossingCounter.prototype.getLocation = function getLocation () {
96161           if (this._isPointOnSegment) { return Location.BOUNDARY }
96162           if (this._crossingCount % 2 === 1) {
96163             return Location.INTERIOR
96164           }
96165           return Location.EXTERIOR
96166         };
96167         RayCrossingCounter.prototype.isOnSegment = function isOnSegment () {
96168           return this._isPointOnSegment
96169         };
96170         RayCrossingCounter.prototype.interfaces_ = function interfaces_ () {
96171           return []
96172         };
96173         RayCrossingCounter.prototype.getClass = function getClass () {
96174           return RayCrossingCounter
96175         };
96176         RayCrossingCounter.locatePointInRing = function locatePointInRing () {
96177           if (arguments[0] instanceof Coordinate && hasInterface(arguments[1], CoordinateSequence)) {
96178             var p = arguments[0];
96179             var ring = arguments[1];
96180             var counter = new RayCrossingCounter(p);
96181             var p1 = new Coordinate();
96182             var p2 = new Coordinate();
96183             for (var i = 1; i < ring.size(); i++) {
96184               ring.getCoordinate(i, p1);
96185               ring.getCoordinate(i - 1, p2);
96186               counter.countSegment(p1, p2);
96187               if (counter.isOnSegment()) { return counter.getLocation() }
96188             }
96189             return counter.getLocation()
96190           } else if (arguments[0] instanceof Coordinate && arguments[1] instanceof Array) {
96191             var p$1 = arguments[0];
96192             var ring$1 = arguments[1];
96193             var counter$1 = new RayCrossingCounter(p$1);
96194             for (var i$1 = 1; i$1 < ring$1.length; i$1++) {
96195               var p1$1 = ring$1[i$1];
96196               var p2$1 = ring$1[i$1 - 1];
96197               counter$1.countSegment(p1$1, p2$1);
96198               if (counter$1.isOnSegment()) { return counter$1.getLocation() }
96199             }
96200             return counter$1.getLocation()
96201           }
96202         };
96203
96204         var CGAlgorithms = function CGAlgorithms () {};
96205
96206         var staticAccessors$3 = { CLOCKWISE: { configurable: true },RIGHT: { configurable: true },COUNTERCLOCKWISE: { configurable: true },LEFT: { configurable: true },COLLINEAR: { configurable: true },STRAIGHT: { configurable: true } };
96207
96208         CGAlgorithms.prototype.interfaces_ = function interfaces_ () {
96209           return []
96210         };
96211         CGAlgorithms.prototype.getClass = function getClass () {
96212           return CGAlgorithms
96213         };
96214         CGAlgorithms.orientationIndex = function orientationIndex (p1, p2, q) {
96215           return CGAlgorithmsDD.orientationIndex(p1, p2, q)
96216         };
96217         CGAlgorithms.signedArea = function signedArea () {
96218           if (arguments[0] instanceof Array) {
96219             var ring = arguments[0];
96220             if (ring.length < 3) { return 0.0 }
96221             var sum = 0.0;
96222             var x0 = ring[0].x;
96223             for (var i = 1; i < ring.length - 1; i++) {
96224               var x = ring[i].x - x0;
96225               var y1 = ring[i + 1].y;
96226               var y2 = ring[i - 1].y;
96227               sum += x * (y2 - y1);
96228             }
96229             return sum / 2.0
96230           } else if (hasInterface(arguments[0], CoordinateSequence)) {
96231             var ring$1 = arguments[0];
96232             var n = ring$1.size();
96233             if (n < 3) { return 0.0 }
96234             var p0 = new Coordinate();
96235             var p1 = new Coordinate();
96236             var p2 = new Coordinate();
96237             ring$1.getCoordinate(0, p1);
96238             ring$1.getCoordinate(1, p2);
96239             var x0$1 = p1.x;
96240             p2.x -= x0$1;
96241             var sum$1 = 0.0;
96242             for (var i$1 = 1; i$1 < n - 1; i$1++) {
96243               p0.y = p1.y;
96244               p1.x = p2.x;
96245               p1.y = p2.y;
96246               ring$1.getCoordinate(i$1 + 1, p2);
96247               p2.x -= x0$1;
96248               sum$1 += p1.x * (p0.y - p2.y);
96249             }
96250             return sum$1 / 2.0
96251           }
96252         };
96253         CGAlgorithms.distanceLineLine = function distanceLineLine (A, B, C, D) {
96254           if (A.equals(B)) { return CGAlgorithms.distancePointLine(A, C, D) }
96255           if (C.equals(D)) { return CGAlgorithms.distancePointLine(D, A, B) }
96256           var noIntersection = false;
96257           if (!Envelope.intersects(A, B, C, D)) {
96258             noIntersection = true;
96259           } else {
96260             var denom = (B.x - A.x) * (D.y - C.y) - (B.y - A.y) * (D.x - C.x);
96261             if (denom === 0) {
96262               noIntersection = true;
96263             } else {
96264               var rNumb = (A.y - C.y) * (D.x - C.x) - (A.x - C.x) * (D.y - C.y);
96265               var sNum = (A.y - C.y) * (B.x - A.x) - (A.x - C.x) * (B.y - A.y);
96266               var s = sNum / denom;
96267               var r = rNumb / denom;
96268               if (r < 0 || r > 1 || s < 0 || s > 1) {
96269                 noIntersection = true;
96270               }
96271             }
96272           }
96273           if (noIntersection) {
96274             return MathUtil.min(CGAlgorithms.distancePointLine(A, C, D), CGAlgorithms.distancePointLine(B, C, D), CGAlgorithms.distancePointLine(C, A, B), CGAlgorithms.distancePointLine(D, A, B))
96275           }
96276           return 0.0
96277         };
96278         CGAlgorithms.isPointInRing = function isPointInRing (p, ring) {
96279           return CGAlgorithms.locatePointInRing(p, ring) !== Location.EXTERIOR
96280         };
96281         CGAlgorithms.computeLength = function computeLength (pts) {
96282           var n = pts.size();
96283           if (n <= 1) { return 0.0 }
96284           var len = 0.0;
96285           var p = new Coordinate();
96286           pts.getCoordinate(0, p);
96287           var x0 = p.x;
96288           var y0 = p.y;
96289           for (var i = 1; i < n; i++) {
96290             pts.getCoordinate(i, p);
96291             var x1 = p.x;
96292             var y1 = p.y;
96293             var dx = x1 - x0;
96294             var dy = y1 - y0;
96295             len += Math.sqrt(dx * dx + dy * dy);
96296             x0 = x1;
96297             y0 = y1;
96298           }
96299           return len
96300         };
96301         CGAlgorithms.isCCW = function isCCW (ring) {
96302           var nPts = ring.length - 1;
96303           if (nPts < 3) { throw new IllegalArgumentException('Ring has fewer than 4 points, so orientation cannot be determined') }
96304           var hiPt = ring[0];
96305           var hiIndex = 0;
96306           for (var i = 1; i <= nPts; i++) {
96307             var p = ring[i];
96308             if (p.y > hiPt.y) {
96309               hiPt = p;
96310               hiIndex = i;
96311             }
96312           }
96313           var iPrev = hiIndex;
96314           do {
96315             iPrev = iPrev - 1;
96316             if (iPrev < 0) { iPrev = nPts; }
96317           } while (ring[iPrev].equals2D(hiPt) && iPrev !== hiIndex)
96318           var iNext = hiIndex;
96319           do {
96320             iNext = (iNext + 1) % nPts;
96321           } while (ring[iNext].equals2D(hiPt) && iNext !== hiIndex)
96322           var prev = ring[iPrev];
96323           var next = ring[iNext];
96324           if (prev.equals2D(hiPt) || next.equals2D(hiPt) || prev.equals2D(next)) { return false }
96325           var disc = CGAlgorithms.computeOrientation(prev, hiPt, next);
96326           var isCCW = false;
96327           if (disc === 0) {
96328             isCCW = prev.x > next.x;
96329           } else {
96330             isCCW = disc > 0;
96331           }
96332           return isCCW
96333         };
96334         CGAlgorithms.locatePointInRing = function locatePointInRing (p, ring) {
96335           return RayCrossingCounter.locatePointInRing(p, ring)
96336         };
96337         CGAlgorithms.distancePointLinePerpendicular = function distancePointLinePerpendicular (p, A, B) {
96338           var len2 = (B.x - A.x) * (B.x - A.x) + (B.y - A.y) * (B.y - A.y);
96339           var s = ((A.y - p.y) * (B.x - A.x) - (A.x - p.x) * (B.y - A.y)) / len2;
96340           return Math.abs(s) * Math.sqrt(len2)
96341         };
96342         CGAlgorithms.computeOrientation = function computeOrientation (p1, p2, q) {
96343           return CGAlgorithms.orientationIndex(p1, p2, q)
96344         };
96345         CGAlgorithms.distancePointLine = function distancePointLine () {
96346           if (arguments.length === 2) {
96347             var p = arguments[0];
96348             var line = arguments[1];
96349             if (line.length === 0) { throw new IllegalArgumentException('Line array must contain at least one vertex') }
96350             var minDistance = p.distance(line[0]);
96351             for (var i = 0; i < line.length - 1; i++) {
96352               var dist = CGAlgorithms.distancePointLine(p, line[i], line[i + 1]);
96353               if (dist < minDistance) {
96354                 minDistance = dist;
96355               }
96356             }
96357             return minDistance
96358           } else if (arguments.length === 3) {
96359             var p$1 = arguments[0];
96360             var A = arguments[1];
96361             var B = arguments[2];
96362             if (A.x === B.x && A.y === B.y) { return p$1.distance(A) }
96363             var len2 = (B.x - A.x) * (B.x - A.x) + (B.y - A.y) * (B.y - A.y);
96364             var r = ((p$1.x - A.x) * (B.x - A.x) + (p$1.y - A.y) * (B.y - A.y)) / len2;
96365             if (r <= 0.0) { return p$1.distance(A) }
96366             if (r >= 1.0) { return p$1.distance(B) }
96367             var s = ((A.y - p$1.y) * (B.x - A.x) - (A.x - p$1.x) * (B.y - A.y)) / len2;
96368             return Math.abs(s) * Math.sqrt(len2)
96369           }
96370         };
96371         CGAlgorithms.isOnLine = function isOnLine (p, pt) {
96372           var lineIntersector = new RobustLineIntersector();
96373           for (var i = 1; i < pt.length; i++) {
96374             var p0 = pt[i - 1];
96375             var p1 = pt[i];
96376             lineIntersector.computeIntersection(p, p0, p1);
96377             if (lineIntersector.hasIntersection()) {
96378               return true
96379             }
96380           }
96381           return false
96382         };
96383         staticAccessors$3.CLOCKWISE.get = function () { return -1 };
96384         staticAccessors$3.RIGHT.get = function () { return CGAlgorithms.CLOCKWISE };
96385         staticAccessors$3.COUNTERCLOCKWISE.get = function () { return 1 };
96386         staticAccessors$3.LEFT.get = function () { return CGAlgorithms.COUNTERCLOCKWISE };
96387         staticAccessors$3.COLLINEAR.get = function () { return 0 };
96388         staticAccessors$3.STRAIGHT.get = function () { return CGAlgorithms.COLLINEAR };
96389
96390         Object.defineProperties( CGAlgorithms, staticAccessors$3 );
96391
96392         var GeometryComponentFilter = function GeometryComponentFilter () {};
96393
96394         GeometryComponentFilter.prototype.filter = function filter (geom) {};
96395         GeometryComponentFilter.prototype.interfaces_ = function interfaces_ () {
96396           return []
96397         };
96398         GeometryComponentFilter.prototype.getClass = function getClass () {
96399           return GeometryComponentFilter
96400         };
96401
96402         var Geometry = function Geometry () {
96403           var factory = arguments[0];
96404
96405           this._envelope = null;
96406           this._factory = null;
96407           this._SRID = null;
96408           this._userData = null;
96409           this._factory = factory;
96410           this._SRID = factory.getSRID();
96411         };
96412
96413         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 } };
96414         Geometry.prototype.isGeometryCollection = function isGeometryCollection () {
96415           return this.getSortIndex() === Geometry.SORTINDEX_GEOMETRYCOLLECTION
96416         };
96417         Geometry.prototype.getFactory = function getFactory () {
96418           return this._factory
96419         };
96420         Geometry.prototype.getGeometryN = function getGeometryN (n) {
96421           return this
96422         };
96423         Geometry.prototype.getArea = function getArea () {
96424           return 0.0
96425         };
96426         Geometry.prototype.isRectangle = function isRectangle () {
96427           return false
96428         };
96429         Geometry.prototype.equals = function equals () {
96430           if (arguments[0] instanceof Geometry) {
96431             var g$1 = arguments[0];
96432             if (g$1 === null) { return false }
96433             return this.equalsTopo(g$1)
96434           } else if (arguments[0] instanceof Object) {
96435             var o = arguments[0];
96436             if (!(o instanceof Geometry)) { return false }
96437             var g = o;
96438             return this.equalsExact(g)
96439           }
96440         };
96441         Geometry.prototype.equalsExact = function equalsExact (other) {
96442           return this === other || this.equalsExact(other, 0)
96443         };
96444         Geometry.prototype.geometryChanged = function geometryChanged () {
96445           this.apply(Geometry.geometryChangedFilter);
96446         };
96447         Geometry.prototype.geometryChangedAction = function geometryChangedAction () {
96448           this._envelope = null;
96449         };
96450         Geometry.prototype.equalsNorm = function equalsNorm (g) {
96451           if (g === null) { return false }
96452           return this.norm().equalsExact(g.norm())
96453         };
96454         Geometry.prototype.getLength = function getLength () {
96455           return 0.0
96456         };
96457         Geometry.prototype.getNumGeometries = function getNumGeometries () {
96458           return 1
96459         };
96460         Geometry.prototype.compareTo = function compareTo () {
96461           if (arguments.length === 1) {
96462             var o = arguments[0];
96463             var other = o;
96464             if (this.getSortIndex() !== other.getSortIndex()) {
96465               return this.getSortIndex() - other.getSortIndex()
96466             }
96467             if (this.isEmpty() && other.isEmpty()) {
96468               return 0
96469             }
96470             if (this.isEmpty()) {
96471               return -1
96472             }
96473             if (other.isEmpty()) {
96474               return 1
96475             }
96476             return this.compareToSameClass(o)
96477           } else if (arguments.length === 2) {
96478             var other$1 = arguments[0];
96479             var comp = arguments[1];
96480             if (this.getSortIndex() !== other$1.getSortIndex()) {
96481               return this.getSortIndex() - other$1.getSortIndex()
96482             }
96483             if (this.isEmpty() && other$1.isEmpty()) {
96484               return 0
96485             }
96486             if (this.isEmpty()) {
96487               return -1
96488             }
96489             if (other$1.isEmpty()) {
96490               return 1
96491             }
96492             return this.compareToSameClass(other$1, comp)
96493           }
96494         };
96495         Geometry.prototype.getUserData = function getUserData () {
96496           return this._userData
96497         };
96498         Geometry.prototype.getSRID = function getSRID () {
96499           return this._SRID
96500         };
96501         Geometry.prototype.getEnvelope = function getEnvelope () {
96502           return this.getFactory().toGeometry(this.getEnvelopeInternal())
96503         };
96504         Geometry.prototype.checkNotGeometryCollection = function checkNotGeometryCollection (g) {
96505           if (g.getSortIndex() === Geometry.SORTINDEX_GEOMETRYCOLLECTION) {
96506             throw new IllegalArgumentException('This method does not support GeometryCollection arguments')
96507           }
96508         };
96509         Geometry.prototype.equal = function equal (a, b, tolerance) {
96510           if (tolerance === 0) {
96511             return a.equals(b)
96512           }
96513           return a.distance(b) <= tolerance
96514         };
96515         Geometry.prototype.norm = function norm () {
96516           var copy = this.copy();
96517           copy.normalize();
96518           return copy
96519         };
96520         Geometry.prototype.getPrecisionModel = function getPrecisionModel () {
96521           return this._factory.getPrecisionModel()
96522         };
96523         Geometry.prototype.getEnvelopeInternal = function getEnvelopeInternal () {
96524           if (this._envelope === null) {
96525             this._envelope = this.computeEnvelopeInternal();
96526           }
96527           return new Envelope(this._envelope)
96528         };
96529         Geometry.prototype.setSRID = function setSRID (SRID) {
96530           this._SRID = SRID;
96531         };
96532         Geometry.prototype.setUserData = function setUserData (userData) {
96533           this._userData = userData;
96534         };
96535         Geometry.prototype.compare = function compare (a, b) {
96536           var i = a.iterator();
96537           var j = b.iterator();
96538           while (i.hasNext() && j.hasNext()) {
96539             var aElement = i.next();
96540             var bElement = j.next();
96541             var comparison = aElement.compareTo(bElement);
96542             if (comparison !== 0) {
96543               return comparison
96544             }
96545           }
96546           if (i.hasNext()) {
96547             return 1
96548           }
96549           if (j.hasNext()) {
96550             return -1
96551           }
96552           return 0
96553         };
96554         Geometry.prototype.hashCode = function hashCode () {
96555           return this.getEnvelopeInternal().hashCode()
96556         };
96557         Geometry.prototype.isGeometryCollectionOrDerived = function isGeometryCollectionOrDerived () {
96558           if (this.getSortIndex() === Geometry.SORTINDEX_GEOMETRYCOLLECTION || this.getSortIndex() === Geometry.SORTINDEX_MULTIPOINT || this.getSortIndex() === Geometry.SORTINDEX_MULTILINESTRING || this.getSortIndex() === Geometry.SORTINDEX_MULTIPOLYGON) {
96559             return true
96560           }
96561           return false
96562         };
96563         Geometry.prototype.interfaces_ = function interfaces_ () {
96564           return [Clonable, Comparable, Serializable]
96565         };
96566         Geometry.prototype.getClass = function getClass () {
96567           return Geometry
96568         };
96569         Geometry.hasNonEmptyElements = function hasNonEmptyElements (geometries) {
96570           for (var i = 0; i < geometries.length; i++) {
96571             if (!geometries[i].isEmpty()) {
96572               return true
96573             }
96574           }
96575           return false
96576         };
96577         Geometry.hasNullElements = function hasNullElements (array) {
96578           for (var i = 0; i < array.length; i++) {
96579             if (array[i] === null) {
96580               return true
96581             }
96582           }
96583           return false
96584         };
96585         staticAccessors$11.serialVersionUID.get = function () { return 8763622679187376702 };
96586         staticAccessors$11.SORTINDEX_POINT.get = function () { return 0 };
96587         staticAccessors$11.SORTINDEX_MULTIPOINT.get = function () { return 1 };
96588         staticAccessors$11.SORTINDEX_LINESTRING.get = function () { return 2 };
96589         staticAccessors$11.SORTINDEX_LINEARRING.get = function () { return 3 };
96590         staticAccessors$11.SORTINDEX_MULTILINESTRING.get = function () { return 4 };
96591         staticAccessors$11.SORTINDEX_POLYGON.get = function () { return 5 };
96592         staticAccessors$11.SORTINDEX_MULTIPOLYGON.get = function () { return 6 };
96593         staticAccessors$11.SORTINDEX_GEOMETRYCOLLECTION.get = function () { return 7 };
96594         staticAccessors$11.geometryChangedFilter.get = function () { return geometryChangedFilter };
96595
96596         Object.defineProperties( Geometry, staticAccessors$11 );
96597
96598         var geometryChangedFilter = function geometryChangedFilter () {};
96599
96600         geometryChangedFilter.interfaces_ = function interfaces_ () {
96601           return [GeometryComponentFilter]
96602         };
96603         geometryChangedFilter.filter = function filter (geom) {
96604           geom.geometryChangedAction();
96605         };
96606
96607         var CoordinateFilter = function CoordinateFilter () {};
96608
96609         CoordinateFilter.prototype.filter = function filter (coord) {};
96610         CoordinateFilter.prototype.interfaces_ = function interfaces_ () {
96611           return []
96612         };
96613         CoordinateFilter.prototype.getClass = function getClass () {
96614           return CoordinateFilter
96615         };
96616
96617         var BoundaryNodeRule = function BoundaryNodeRule () {};
96618
96619         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 } };
96620
96621         BoundaryNodeRule.prototype.isInBoundary = function isInBoundary (boundaryCount) {};
96622         BoundaryNodeRule.prototype.interfaces_ = function interfaces_ () {
96623           return []
96624         };
96625         BoundaryNodeRule.prototype.getClass = function getClass () {
96626           return BoundaryNodeRule
96627         };
96628         staticAccessors$12.Mod2BoundaryNodeRule.get = function () { return Mod2BoundaryNodeRule };
96629         staticAccessors$12.EndPointBoundaryNodeRule.get = function () { return EndPointBoundaryNodeRule };
96630         staticAccessors$12.MultiValentEndPointBoundaryNodeRule.get = function () { return MultiValentEndPointBoundaryNodeRule };
96631         staticAccessors$12.MonoValentEndPointBoundaryNodeRule.get = function () { return MonoValentEndPointBoundaryNodeRule };
96632         staticAccessors$12.MOD2_BOUNDARY_RULE.get = function () { return new Mod2BoundaryNodeRule() };
96633         staticAccessors$12.ENDPOINT_BOUNDARY_RULE.get = function () { return new EndPointBoundaryNodeRule() };
96634         staticAccessors$12.MULTIVALENT_ENDPOINT_BOUNDARY_RULE.get = function () { return new MultiValentEndPointBoundaryNodeRule() };
96635         staticAccessors$12.MONOVALENT_ENDPOINT_BOUNDARY_RULE.get = function () { return new MonoValentEndPointBoundaryNodeRule() };
96636         staticAccessors$12.OGC_SFS_BOUNDARY_RULE.get = function () { return BoundaryNodeRule.MOD2_BOUNDARY_RULE };
96637
96638         Object.defineProperties( BoundaryNodeRule, staticAccessors$12 );
96639
96640         var Mod2BoundaryNodeRule = function Mod2BoundaryNodeRule () {};
96641
96642         Mod2BoundaryNodeRule.prototype.isInBoundary = function isInBoundary (boundaryCount) {
96643           return boundaryCount % 2 === 1
96644         };
96645         Mod2BoundaryNodeRule.prototype.interfaces_ = function interfaces_ () {
96646           return [BoundaryNodeRule]
96647         };
96648         Mod2BoundaryNodeRule.prototype.getClass = function getClass () {
96649           return Mod2BoundaryNodeRule
96650         };
96651
96652         var EndPointBoundaryNodeRule = function EndPointBoundaryNodeRule () {};
96653
96654         EndPointBoundaryNodeRule.prototype.isInBoundary = function isInBoundary (boundaryCount) {
96655           return boundaryCount > 0
96656         };
96657         EndPointBoundaryNodeRule.prototype.interfaces_ = function interfaces_ () {
96658           return [BoundaryNodeRule]
96659         };
96660         EndPointBoundaryNodeRule.prototype.getClass = function getClass () {
96661           return EndPointBoundaryNodeRule
96662         };
96663
96664         var MultiValentEndPointBoundaryNodeRule = function MultiValentEndPointBoundaryNodeRule () {};
96665
96666         MultiValentEndPointBoundaryNodeRule.prototype.isInBoundary = function isInBoundary (boundaryCount) {
96667           return boundaryCount > 1
96668         };
96669         MultiValentEndPointBoundaryNodeRule.prototype.interfaces_ = function interfaces_ () {
96670           return [BoundaryNodeRule]
96671         };
96672         MultiValentEndPointBoundaryNodeRule.prototype.getClass = function getClass () {
96673           return MultiValentEndPointBoundaryNodeRule
96674         };
96675
96676         var MonoValentEndPointBoundaryNodeRule = function MonoValentEndPointBoundaryNodeRule () {};
96677
96678         MonoValentEndPointBoundaryNodeRule.prototype.isInBoundary = function isInBoundary (boundaryCount) {
96679           return boundaryCount === 1
96680         };
96681         MonoValentEndPointBoundaryNodeRule.prototype.interfaces_ = function interfaces_ () {
96682           return [BoundaryNodeRule]
96683         };
96684         MonoValentEndPointBoundaryNodeRule.prototype.getClass = function getClass () {
96685           return MonoValentEndPointBoundaryNodeRule
96686         };
96687
96688         // import Iterator from './Iterator'
96689
96690         /**
96691          * @see http://download.oracle.com/javase/6/docs/api/java/util/Collection.html
96692          *
96693          * @constructor
96694          * @private
96695          */
96696         var Collection = function Collection () {};
96697
96698         Collection.prototype.add = function add () {};
96699
96700         /**
96701          * Appends all of the elements in the specified collection to the end of this
96702          * list, in the order that they are returned by the specified collection's
96703          * iterator (optional operation).
96704          * @param {javascript.util.Collection} c
96705          * @return {boolean}
96706          */
96707         Collection.prototype.addAll = function addAll () {};
96708
96709         /**
96710          * Returns true if this collection contains no elements.
96711          * @return {boolean}
96712          */
96713         Collection.prototype.isEmpty = function isEmpty () {};
96714
96715         /**
96716          * Returns an iterator over the elements in this collection.
96717          * @return {javascript.util.Iterator}
96718          */
96719         Collection.prototype.iterator = function iterator () {};
96720
96721         /**
96722          * Returns an iterator over the elements in this collection.
96723          * @return {number}
96724          */
96725         Collection.prototype.size = function size () {};
96726
96727         /**
96728          * Returns an array containing all of the elements in this collection.
96729          * @return {Array}
96730          */
96731         Collection.prototype.toArray = function toArray () {};
96732
96733         /**
96734          * Removes a single instance of the specified element from this collection if it
96735          * is present. (optional)
96736          * @param {Object} e
96737          * @return {boolean}
96738          */
96739         Collection.prototype.remove = function remove () {};
96740
96741         /**
96742          * @param {string=} message Optional message
96743          * @extends {Error}
96744          * @constructor
96745          * @private
96746          */
96747         function IndexOutOfBoundsException (message) {
96748           this.message = message || '';
96749         }
96750         IndexOutOfBoundsException.prototype = new Error();
96751
96752         /**
96753          * @type {string}
96754          */
96755         IndexOutOfBoundsException.prototype.name = 'IndexOutOfBoundsException';
96756
96757         /**
96758          * @see http://download.oracle.com/javase/6/docs/api/java/util/Iterator.html
96759          * @constructor
96760          * @private
96761          */
96762         var Iterator$1 = function Iterator () {};
96763
96764         Iterator$1.prototype.hasNext = function hasNext () {};
96765
96766         /**
96767          * Returns the next element in the iteration.
96768          * @return {Object}
96769          */
96770         Iterator$1.prototype.next = function next () {};
96771
96772         /**
96773          * Removes from the underlying collection the last element returned by the
96774          * iterator (optional operation).
96775          */
96776         Iterator$1.prototype.remove = function remove () {};
96777
96778         /**
96779          * @see http://download.oracle.com/javase/6/docs/api/java/util/List.html
96780          *
96781          * @extends {javascript.util.Collection}
96782          * @constructor
96783          * @private
96784          */
96785         var List = (function (Collection$$1) {
96786           function List () {
96787             Collection$$1.apply(this, arguments);
96788           }
96789
96790           if ( Collection$$1 ) { List.__proto__ = Collection$$1; }
96791           List.prototype = Object.create( Collection$$1 && Collection$$1.prototype );
96792           List.prototype.constructor = List;
96793
96794           List.prototype.get = function get () { };
96795
96796           /**
96797            * Replaces the element at the specified position in this list with the
96798            * specified element (optional operation).
96799            * @param {number} index
96800            * @param {Object} e
96801            * @return {Object}
96802            */
96803           List.prototype.set = function set () { };
96804
96805           /**
96806            * Returns true if this collection contains no elements.
96807            * @return {boolean}
96808            */
96809           List.prototype.isEmpty = function isEmpty () { };
96810
96811           return List;
96812         }(Collection));
96813
96814         /**
96815          * @param {string=} message Optional message
96816          * @extends {Error}
96817          * @constructor
96818          * @private
96819          */
96820         function NoSuchElementException (message) {
96821           this.message = message || '';
96822         }
96823         NoSuchElementException.prototype = new Error();
96824
96825         /**
96826          * @type {string}
96827          */
96828         NoSuchElementException.prototype.name = 'NoSuchElementException';
96829
96830         // import OperationNotSupported from './OperationNotSupported'
96831
96832         /**
96833          * @see http://download.oracle.com/javase/6/docs/api/java/util/ArrayList.html
96834          *
96835          * @extends List
96836          * @private
96837          */
96838         var ArrayList = (function (List$$1) {
96839           function ArrayList () {
96840             List$$1.call(this);
96841             this.array_ = [];
96842
96843             if (arguments[0] instanceof Collection) {
96844               this.addAll(arguments[0]);
96845             }
96846           }
96847
96848           if ( List$$1 ) { ArrayList.__proto__ = List$$1; }
96849           ArrayList.prototype = Object.create( List$$1 && List$$1.prototype );
96850           ArrayList.prototype.constructor = ArrayList;
96851
96852           ArrayList.prototype.ensureCapacity = function ensureCapacity () {};
96853           ArrayList.prototype.interfaces_ = function interfaces_ () { return [List$$1, Collection] };
96854
96855           /**
96856            * @override
96857            */
96858           ArrayList.prototype.add = function add (e) {
96859             if (arguments.length === 1) {
96860               this.array_.push(e);
96861             } else {
96862               this.array_.splice(arguments[0], arguments[1]);
96863             }
96864             return true
96865           };
96866
96867           ArrayList.prototype.clear = function clear () {
96868             this.array_ = [];
96869           };
96870
96871           /**
96872            * @override
96873            */
96874           ArrayList.prototype.addAll = function addAll (c) {
96875             var this$1 = this;
96876
96877             for (var i = c.iterator(); i.hasNext();) {
96878               this$1.add(i.next());
96879             }
96880             return true
96881           };
96882
96883           /**
96884            * @override
96885            */
96886           ArrayList.prototype.set = function set (index, element) {
96887             var oldElement = this.array_[index];
96888             this.array_[index] = element;
96889             return oldElement
96890           };
96891
96892           /**
96893            * @override
96894            */
96895           ArrayList.prototype.iterator = function iterator () {
96896             return new Iterator_(this)
96897           };
96898
96899           /**
96900            * @override
96901            */
96902           ArrayList.prototype.get = function get (index) {
96903             if (index < 0 || index >= this.size()) {
96904               throw new IndexOutOfBoundsException()
96905             }
96906
96907             return this.array_[index]
96908           };
96909
96910           /**
96911            * @override
96912            */
96913           ArrayList.prototype.isEmpty = function isEmpty () {
96914             return this.array_.length === 0
96915           };
96916
96917           /**
96918            * @override
96919            */
96920           ArrayList.prototype.size = function size () {
96921             return this.array_.length
96922           };
96923
96924           /**
96925            * @override
96926            */
96927           ArrayList.prototype.toArray = function toArray () {
96928             var this$1 = this;
96929
96930             var array = [];
96931
96932             for (var i = 0, len = this.array_.length; i < len; i++) {
96933               array.push(this$1.array_[i]);
96934             }
96935
96936             return array
96937           };
96938
96939           /**
96940            * @override
96941            */
96942           ArrayList.prototype.remove = function remove (o) {
96943             var this$1 = this;
96944
96945             var found = false;
96946
96947             for (var i = 0, len = this.array_.length; i < len; i++) {
96948               if (this$1.array_[i] === o) {
96949                 this$1.array_.splice(i, 1);
96950                 found = true;
96951                 break
96952               }
96953             }
96954
96955             return found
96956           };
96957
96958           return ArrayList;
96959         }(List));
96960
96961         /**
96962          * @extends {Iterator}
96963          * @param {ArrayList} arrayList
96964          * @constructor
96965          * @private
96966          */
96967         var Iterator_ = (function (Iterator$$1) {
96968           function Iterator_ (arrayList) {
96969             Iterator$$1.call(this);
96970             /**
96971              * @type {ArrayList}
96972              * @private
96973             */
96974             this.arrayList_ = arrayList;
96975             /**
96976              * @type {number}
96977              * @private
96978             */
96979             this.position_ = 0;
96980           }
96981
96982           if ( Iterator$$1 ) { Iterator_.__proto__ = Iterator$$1; }
96983           Iterator_.prototype = Object.create( Iterator$$1 && Iterator$$1.prototype );
96984           Iterator_.prototype.constructor = Iterator_;
96985
96986           /**
96987            * @override
96988            */
96989           Iterator_.prototype.next = function next () {
96990             if (this.position_ === this.arrayList_.size()) {
96991               throw new NoSuchElementException()
96992             }
96993             return this.arrayList_.get(this.position_++)
96994           };
96995
96996           /**
96997            * @override
96998            */
96999           Iterator_.prototype.hasNext = function hasNext () {
97000             if (this.position_ < this.arrayList_.size()) {
97001               return true
97002             } else {
97003               return false
97004             }
97005           };
97006
97007           /**
97008            * TODO: should be in ListIterator
97009            * @override
97010            */
97011           Iterator_.prototype.set = function set (element) {
97012             return this.arrayList_.set(this.position_ - 1, element)
97013           };
97014
97015           /**
97016            * @override
97017            */
97018           Iterator_.prototype.remove = function remove () {
97019             this.arrayList_.remove(this.arrayList_.get(this.position_));
97020           };
97021
97022           return Iterator_;
97023         }(Iterator$1));
97024
97025         var CoordinateList = (function (ArrayList$$1) {
97026           function CoordinateList () {
97027             ArrayList$$1.call(this);
97028             if (arguments.length === 0) ; else if (arguments.length === 1) {
97029               var coord = arguments[0];
97030               this.ensureCapacity(coord.length);
97031               this.add(coord, true);
97032             } else if (arguments.length === 2) {
97033               var coord$1 = arguments[0];
97034               var allowRepeated = arguments[1];
97035               this.ensureCapacity(coord$1.length);
97036               this.add(coord$1, allowRepeated);
97037             }
97038           }
97039
97040           if ( ArrayList$$1 ) { CoordinateList.__proto__ = ArrayList$$1; }
97041           CoordinateList.prototype = Object.create( ArrayList$$1 && ArrayList$$1.prototype );
97042           CoordinateList.prototype.constructor = CoordinateList;
97043
97044           var staticAccessors = { coordArrayType: { configurable: true } };
97045           staticAccessors.coordArrayType.get = function () { return new Array(0).fill(null) };
97046           CoordinateList.prototype.getCoordinate = function getCoordinate (i) {
97047             return this.get(i)
97048           };
97049           CoordinateList.prototype.addAll = function addAll () {
97050             var this$1 = this;
97051
97052             if (arguments.length === 2) {
97053               var coll = arguments[0];
97054               var allowRepeated = arguments[1];
97055               var isChanged = false;
97056               for (var i = coll.iterator(); i.hasNext();) {
97057                 this$1.add(i.next(), allowRepeated);
97058                 isChanged = true;
97059               }
97060               return isChanged
97061             } else { return ArrayList$$1.prototype.addAll.apply(this, arguments) }
97062           };
97063           CoordinateList.prototype.clone = function clone () {
97064             var this$1 = this;
97065
97066             var clone = ArrayList$$1.prototype.clone.call(this);
97067             for (var i = 0; i < this.size(); i++) {
97068               clone.add(i, this$1.get(i).copy());
97069             }
97070             return clone
97071           };
97072           CoordinateList.prototype.toCoordinateArray = function toCoordinateArray () {
97073             return this.toArray(CoordinateList.coordArrayType)
97074           };
97075           CoordinateList.prototype.add = function add () {
97076             var this$1 = this;
97077
97078             if (arguments.length === 1) {
97079               var coord = arguments[0];
97080               ArrayList$$1.prototype.add.call(this, coord);
97081             } else if (arguments.length === 2) {
97082               if (arguments[0] instanceof Array && typeof arguments[1] === 'boolean') {
97083                 var coord$1 = arguments[0];
97084                 var allowRepeated = arguments[1];
97085                 this.add(coord$1, allowRepeated, true);
97086                 return true
97087               } else if (arguments[0] instanceof Coordinate && typeof arguments[1] === 'boolean') {
97088                 var coord$2 = arguments[0];
97089                 var allowRepeated$1 = arguments[1];
97090                 if (!allowRepeated$1) {
97091                   if (this.size() >= 1) {
97092                     var last = this.get(this.size() - 1);
97093                     if (last.equals2D(coord$2)) { return null }
97094                   }
97095                 }
97096                 ArrayList$$1.prototype.add.call(this, coord$2);
97097               } else if (arguments[0] instanceof Object && typeof arguments[1] === 'boolean') {
97098                 var obj = arguments[0];
97099                 var allowRepeated$2 = arguments[1];
97100                 this.add(obj, allowRepeated$2);
97101                 return true
97102               }
97103             } else if (arguments.length === 3) {
97104               if (typeof arguments[2] === 'boolean' && (arguments[0] instanceof Array && typeof arguments[1] === 'boolean')) {
97105                 var coord$3 = arguments[0];
97106                 var allowRepeated$3 = arguments[1];
97107                 var direction = arguments[2];
97108                 if (direction) {
97109                   for (var i$1 = 0; i$1 < coord$3.length; i$1++) {
97110                     this$1.add(coord$3[i$1], allowRepeated$3);
97111                   }
97112                 } else {
97113                   for (var i$2 = coord$3.length - 1; i$2 >= 0; i$2--) {
97114                     this$1.add(coord$3[i$2], allowRepeated$3);
97115                   }
97116                 }
97117                 return true
97118               } else if (typeof arguments[2] === 'boolean' && (Number.isInteger(arguments[0]) && arguments[1] instanceof Coordinate)) {
97119                 var i$3 = arguments[0];
97120                 var coord$4 = arguments[1];
97121                 var allowRepeated$4 = arguments[2];
97122                 if (!allowRepeated$4) {
97123                   var size = this.size();
97124                   if (size > 0) {
97125                     if (i$3 > 0) {
97126                       var prev = this.get(i$3 - 1);
97127                       if (prev.equals2D(coord$4)) { return null }
97128                     }
97129                     if (i$3 < size) {
97130                       var next = this.get(i$3);
97131                       if (next.equals2D(coord$4)) { return null }
97132                     }
97133                   }
97134                 }
97135                 ArrayList$$1.prototype.add.call(this, i$3, coord$4);
97136               }
97137             } else if (arguments.length === 4) {
97138               var coord$5 = arguments[0];
97139               var allowRepeated$5 = arguments[1];
97140               var start = arguments[2];
97141               var end = arguments[3];
97142               var inc = 1;
97143               if (start > end) { inc = -1; }
97144               for (var i = start; i !== end; i += inc) {
97145                 this$1.add(coord$5[i], allowRepeated$5);
97146               }
97147               return true
97148             }
97149           };
97150           CoordinateList.prototype.closeRing = function closeRing () {
97151             if (this.size() > 0) { this.add(new Coordinate(this.get(0)), false); }
97152           };
97153           CoordinateList.prototype.interfaces_ = function interfaces_ () {
97154             return []
97155           };
97156           CoordinateList.prototype.getClass = function getClass () {
97157             return CoordinateList
97158           };
97159
97160           Object.defineProperties( CoordinateList, staticAccessors );
97161
97162           return CoordinateList;
97163         }(ArrayList));
97164
97165         var CoordinateArrays = function CoordinateArrays () {};
97166
97167         var staticAccessors$13 = { ForwardComparator: { configurable: true },BidirectionalComparator: { configurable: true },coordArrayType: { configurable: true } };
97168
97169         staticAccessors$13.ForwardComparator.get = function () { return ForwardComparator };
97170         staticAccessors$13.BidirectionalComparator.get = function () { return BidirectionalComparator };
97171         staticAccessors$13.coordArrayType.get = function () { return new Array(0).fill(null) };
97172
97173         CoordinateArrays.prototype.interfaces_ = function interfaces_ () {
97174           return []
97175         };
97176         CoordinateArrays.prototype.getClass = function getClass () {
97177           return CoordinateArrays
97178         };
97179         CoordinateArrays.isRing = function isRing (pts) {
97180           if (pts.length < 4) { return false }
97181           if (!pts[0].equals2D(pts[pts.length - 1])) { return false }
97182           return true
97183         };
97184         CoordinateArrays.ptNotInList = function ptNotInList (testPts, pts) {
97185           for (var i = 0; i < testPts.length; i++) {
97186             var testPt = testPts[i];
97187             if (CoordinateArrays.indexOf(testPt, pts) < 0) { return testPt }
97188           }
97189           return null
97190         };
97191         CoordinateArrays.scroll = function scroll (coordinates, firstCoordinate) {
97192           var i = CoordinateArrays.indexOf(firstCoordinate, coordinates);
97193           if (i < 0) { return null }
97194           var newCoordinates = new Array(coordinates.length).fill(null);
97195           System.arraycopy(coordinates, i, newCoordinates, 0, coordinates.length - i);
97196           System.arraycopy(coordinates, 0, newCoordinates, coordinates.length - i, i);
97197           System.arraycopy(newCoordinates, 0, coordinates, 0, coordinates.length);
97198         };
97199         CoordinateArrays.equals = function equals () {
97200           if (arguments.length === 2) {
97201             var coord1 = arguments[0];
97202             var coord2 = arguments[1];
97203             if (coord1 === coord2) { return true }
97204             if (coord1 === null || coord2 === null) { return false }
97205             if (coord1.length !== coord2.length) { return false }
97206             for (var i = 0; i < coord1.length; i++) {
97207               if (!coord1[i].equals(coord2[i])) { return false }
97208             }
97209             return true
97210           } else if (arguments.length === 3) {
97211             var coord1$1 = arguments[0];
97212             var coord2$1 = arguments[1];
97213             var coordinateComparator = arguments[2];
97214             if (coord1$1 === coord2$1) { return true }
97215             if (coord1$1 === null || coord2$1 === null) { return false }
97216             if (coord1$1.length !== coord2$1.length) { return false }
97217             for (var i$1 = 0; i$1 < coord1$1.length; i$1++) {
97218               if (coordinateComparator.compare(coord1$1[i$1], coord2$1[i$1]) !== 0) { return false }
97219             }
97220             return true
97221           }
97222         };
97223         CoordinateArrays.intersection = function intersection (coordinates, env) {
97224           var coordList = new CoordinateList();
97225           for (var i = 0; i < coordinates.length; i++) {
97226             if (env.intersects(coordinates[i])) { coordList.add(coordinates[i], true); }
97227           }
97228           return coordList.toCoordinateArray()
97229         };
97230         CoordinateArrays.hasRepeatedPoints = function hasRepeatedPoints (coord) {
97231           for (var i = 1; i < coord.length; i++) {
97232             if (coord[i - 1].equals(coord[i])) {
97233               return true
97234             }
97235           }
97236           return false
97237         };
97238         CoordinateArrays.removeRepeatedPoints = function removeRepeatedPoints (coord) {
97239           if (!CoordinateArrays.hasRepeatedPoints(coord)) { return coord }
97240           var coordList = new CoordinateList(coord, false);
97241           return coordList.toCoordinateArray()
97242         };
97243         CoordinateArrays.reverse = function reverse (coord) {
97244           var last = coord.length - 1;
97245           var mid = Math.trunc(last / 2);
97246           for (var i = 0; i <= mid; i++) {
97247             var tmp = coord[i];
97248             coord[i] = coord[last - i];
97249             coord[last - i] = tmp;
97250           }
97251         };
97252         CoordinateArrays.removeNull = function removeNull (coord) {
97253           var nonNull = 0;
97254           for (var i = 0; i < coord.length; i++) {
97255             if (coord[i] !== null) { nonNull++; }
97256           }
97257           var newCoord = new Array(nonNull).fill(null);
97258           if (nonNull === 0) { return newCoord }
97259           var j = 0;
97260           for (var i$1 = 0; i$1 < coord.length; i$1++) {
97261             if (coord[i$1] !== null) { newCoord[j++] = coord[i$1]; }
97262           }
97263           return newCoord
97264         };
97265         CoordinateArrays.copyDeep = function copyDeep () {
97266           if (arguments.length === 1) {
97267             var coordinates = arguments[0];
97268             var copy = new Array(coordinates.length).fill(null);
97269             for (var i = 0; i < coordinates.length; i++) {
97270               copy[i] = new Coordinate(coordinates[i]);
97271             }
97272             return copy
97273           } else if (arguments.length === 5) {
97274             var src = arguments[0];
97275             var srcStart = arguments[1];
97276             var dest = arguments[2];
97277             var destStart = arguments[3];
97278             var length = arguments[4];
97279             for (var i$1 = 0; i$1 < length; i$1++) {
97280               dest[destStart + i$1] = new Coordinate(src[srcStart + i$1]);
97281             }
97282           }
97283         };
97284         CoordinateArrays.isEqualReversed = function isEqualReversed (pts1, pts2) {
97285           for (var i = 0; i < pts1.length; i++) {
97286             var p1 = pts1[i];
97287             var p2 = pts2[pts1.length - i - 1];
97288             if (p1.compareTo(p2) !== 0) { return false }
97289           }
97290           return true
97291         };
97292         CoordinateArrays.envelope = function envelope (coordinates) {
97293           var env = new Envelope();
97294           for (var i = 0; i < coordinates.length; i++) {
97295             env.expandToInclude(coordinates[i]);
97296           }
97297           return env
97298         };
97299         CoordinateArrays.toCoordinateArray = function toCoordinateArray (coordList) {
97300           return coordList.toArray(CoordinateArrays.coordArrayType)
97301         };
97302         CoordinateArrays.atLeastNCoordinatesOrNothing = function atLeastNCoordinatesOrNothing (n, c) {
97303           return c.length >= n ? c : []
97304         };
97305         CoordinateArrays.indexOf = function indexOf (coordinate, coordinates) {
97306           for (var i = 0; i < coordinates.length; i++) {
97307             if (coordinate.equals(coordinates[i])) {
97308               return i
97309             }
97310           }
97311           return -1
97312         };
97313         CoordinateArrays.increasingDirection = function increasingDirection (pts) {
97314           for (var i = 0; i < Math.trunc(pts.length / 2); i++) {
97315             var j = pts.length - 1 - i;
97316             var comp = pts[i].compareTo(pts[j]);
97317             if (comp !== 0) { return comp }
97318           }
97319           return 1
97320         };
97321         CoordinateArrays.compare = function compare (pts1, pts2) {
97322           var i = 0;
97323           while (i < pts1.length && i < pts2.length) {
97324             var compare = pts1[i].compareTo(pts2[i]);
97325             if (compare !== 0) { return compare }
97326             i++;
97327           }
97328           if (i < pts2.length) { return -1 }
97329           if (i < pts1.length) { return 1 }
97330           return 0
97331         };
97332         CoordinateArrays.minCoordinate = function minCoordinate (coordinates) {
97333           var minCoord = null;
97334           for (var i = 0; i < coordinates.length; i++) {
97335             if (minCoord === null || minCoord.compareTo(coordinates[i]) > 0) {
97336               minCoord = coordinates[i];
97337             }
97338           }
97339           return minCoord
97340         };
97341         CoordinateArrays.extract = function extract (pts, start, end) {
97342           start = MathUtil.clamp(start, 0, pts.length);
97343           end = MathUtil.clamp(end, -1, pts.length);
97344           var npts = end - start + 1;
97345           if (end < 0) { npts = 0; }
97346           if (start >= pts.length) { npts = 0; }
97347           if (end < start) { npts = 0; }
97348           var extractPts = new Array(npts).fill(null);
97349           if (npts === 0) { return extractPts }
97350           var iPts = 0;
97351           for (var i = start; i <= end; i++) {
97352             extractPts[iPts++] = pts[i];
97353           }
97354           return extractPts
97355         };
97356
97357         Object.defineProperties( CoordinateArrays, staticAccessors$13 );
97358
97359         var ForwardComparator = function ForwardComparator () {};
97360
97361         ForwardComparator.prototype.compare = function compare (o1, o2) {
97362           var pts1 = o1;
97363           var pts2 = o2;
97364           return CoordinateArrays.compare(pts1, pts2)
97365         };
97366         ForwardComparator.prototype.interfaces_ = function interfaces_ () {
97367           return [Comparator]
97368         };
97369         ForwardComparator.prototype.getClass = function getClass () {
97370           return ForwardComparator
97371         };
97372
97373         var BidirectionalComparator = function BidirectionalComparator () {};
97374
97375         BidirectionalComparator.prototype.compare = function compare (o1, o2) {
97376           var pts1 = o1;
97377           var pts2 = o2;
97378           if (pts1.length < pts2.length) { return -1 }
97379           if (pts1.length > pts2.length) { return 1 }
97380           if (pts1.length === 0) { return 0 }
97381           var forwardComp = CoordinateArrays.compare(pts1, pts2);
97382           var isEqualRev = CoordinateArrays.isEqualReversed(pts1, pts2);
97383           if (isEqualRev) { return 0 }
97384           return forwardComp
97385         };
97386         BidirectionalComparator.prototype.OLDcompare = function OLDcompare (o1, o2) {
97387           var pts1 = o1;
97388           var pts2 = o2;
97389           if (pts1.length < pts2.length) { return -1 }
97390           if (pts1.length > pts2.length) { return 1 }
97391           if (pts1.length === 0) { return 0 }
97392           var dir1 = CoordinateArrays.increasingDirection(pts1);
97393           var dir2 = CoordinateArrays.increasingDirection(pts2);
97394           var i1 = dir1 > 0 ? 0 : pts1.length - 1;
97395           var i2 = dir2 > 0 ? 0 : pts1.length - 1;
97396           for (var i = 0; i < pts1.length; i++) {
97397             var comparePt = pts1[i1].compareTo(pts2[i2]);
97398             if (comparePt !== 0) { return comparePt }
97399             i1 += dir1;
97400             i2 += dir2;
97401           }
97402           return 0
97403         };
97404         BidirectionalComparator.prototype.interfaces_ = function interfaces_ () {
97405           return [Comparator]
97406         };
97407         BidirectionalComparator.prototype.getClass = function getClass () {
97408           return BidirectionalComparator
97409         };
97410
97411         /**
97412          * @see http://download.oracle.com/javase/6/docs/api/java/util/Map.html
97413          *
97414          * @constructor
97415          * @private
97416          */
97417         var Map$1$1 = function Map () {};
97418
97419         Map$1$1.prototype.get = function get () {};
97420         /**
97421          * Associates the specified value with the specified key in this map (optional
97422          * operation).
97423          * @param {Object} key
97424          * @param {Object} value
97425          * @return {Object}
97426          */
97427         Map$1$1.prototype.put = function put () {};
97428
97429         /**
97430          * Returns the number of key-value mappings in this map.
97431          * @return {number}
97432          */
97433         Map$1$1.prototype.size = function size () {};
97434
97435         /**
97436          * Returns a Collection view of the values contained in this map.
97437          * @return {javascript.util.Collection}
97438          */
97439         Map$1$1.prototype.values = function values () {};
97440
97441         /**
97442          * Returns a {@link Set} view of the mappings contained in this map.
97443          * The set is backed by the map, so changes to the map are
97444          * reflected in the set, and vice-versa.If the map is modified
97445          * while an iteration over the set is in progress (except through
97446          * the iterator's own <tt>remove</tt> operation, or through the
97447          * <tt>setValue</tt> operation on a map entry returned by the
97448          * iterator) the results of the iteration are undefined.The set
97449          * supports element removal, which removes the corresponding
97450          * mapping from the map, via the <tt>Iterator.remove</tt>,
97451          * <tt>Set.remove</tt>, <tt>removeAll</tt>, <tt>retainAll</tt> and
97452          * <tt>clear</tt> operations.It does not support the
97453          * <tt>add</tt> or <tt>addAll</tt> operations.
97454          *
97455          * @return {Set} a set view of the mappings contained in this map
97456          */
97457         Map$1$1.prototype.entrySet = function entrySet () {};
97458
97459         /**
97460          * @see http://download.oracle.com/javase/6/docs/api/java/util/SortedMap.html
97461          *
97462          * @extends {Map}
97463          * @constructor
97464          * @private
97465          */
97466         var SortedMap = (function (Map) {
97467                 function SortedMap () {
97468                         Map.apply(this, arguments);
97469                 }if ( Map ) { SortedMap.__proto__ = Map; }
97470                 SortedMap.prototype = Object.create( Map && Map.prototype );
97471                 SortedMap.prototype.constructor = SortedMap;
97472
97473                 
97474
97475                 return SortedMap;
97476         }(Map$1$1));
97477
97478         /**
97479          * @param {string=} message Optional message
97480          * @extends {Error}
97481          * @constructor
97482          * @private
97483          */
97484         function OperationNotSupported (message) {
97485           this.message = message || '';
97486         }
97487         OperationNotSupported.prototype = new Error();
97488
97489         /**
97490          * @type {string}
97491          */
97492         OperationNotSupported.prototype.name = 'OperationNotSupported';
97493
97494         /**
97495          * @see http://download.oracle.com/javase/6/docs/api/java/util/Set.html
97496          *
97497          * @extends {Collection}
97498          * @constructor
97499          * @private
97500          */
97501         function Set$2() {}
97502         Set$2.prototype = new Collection();
97503
97504
97505         /**
97506          * Returns true if this set contains the specified element. More formally,
97507          * returns true if and only if this set contains an element e such that (o==null ?
97508          * e==null : o.equals(e)).
97509          * @param {Object} e
97510          * @return {boolean}
97511          */
97512         Set$2.prototype.contains = function() {};
97513
97514         /**
97515          * @see http://docs.oracle.com/javase/6/docs/api/java/util/HashSet.html
97516          *
97517          * @extends {javascript.util.Set}
97518          * @constructor
97519          * @private
97520          */
97521         var HashSet = (function (Set$$1) {
97522           function HashSet () {
97523             Set$$1.call(this);
97524             this.array_ = [];
97525
97526             if (arguments[0] instanceof Collection) {
97527               this.addAll(arguments[0]);
97528             }
97529           }
97530
97531           if ( Set$$1 ) { HashSet.__proto__ = Set$$1; }
97532           HashSet.prototype = Object.create( Set$$1 && Set$$1.prototype );
97533           HashSet.prototype.constructor = HashSet;
97534
97535           /**
97536            * @override
97537            */
97538           HashSet.prototype.contains = function contains (o) {
97539             var this$1 = this;
97540
97541             for (var i = 0, len = this.array_.length; i < len; i++) {
97542               var e = this$1.array_[i];
97543               if (e === o) {
97544                 return true
97545               }
97546             }
97547             return false
97548           };
97549
97550           /**
97551            * @override
97552            */
97553           HashSet.prototype.add = function add (o) {
97554             if (this.contains(o)) {
97555               return false
97556             }
97557
97558             this.array_.push(o);
97559
97560             return true
97561           };
97562
97563           /**
97564            * @override
97565            */
97566           HashSet.prototype.addAll = function addAll (c) {
97567             var this$1 = this;
97568
97569             for (var i = c.iterator(); i.hasNext();) {
97570               this$1.add(i.next());
97571             }
97572             return true
97573           };
97574
97575           /**
97576            * @override
97577            */
97578           HashSet.prototype.remove = function remove (o) {
97579             // throw new javascript.util.OperationNotSupported()
97580             throw new Error()
97581           };
97582
97583           /**
97584            * @override
97585            */
97586           HashSet.prototype.size = function size () {
97587             return this.array_.length
97588           };
97589
97590           /**
97591            * @override
97592            */
97593           HashSet.prototype.isEmpty = function isEmpty () {
97594             return this.array_.length === 0
97595           };
97596
97597           /**
97598            * @override
97599            */
97600           HashSet.prototype.toArray = function toArray () {
97601             var this$1 = this;
97602
97603             var array = [];
97604
97605             for (var i = 0, len = this.array_.length; i < len; i++) {
97606               array.push(this$1.array_[i]);
97607             }
97608
97609             return array
97610           };
97611
97612           /**
97613            * @override
97614            */
97615           HashSet.prototype.iterator = function iterator () {
97616             return new Iterator_$1(this)
97617           };
97618
97619           return HashSet;
97620         }(Set$2));
97621
97622         /**
97623            * @extends {Iterator}
97624            * @param {HashSet} hashSet
97625            * @constructor
97626            * @private
97627            */
97628         var Iterator_$1 = (function (Iterator$$1) {
97629           function Iterator_ (hashSet) {
97630             Iterator$$1.call(this);
97631             /**
97632              * @type {HashSet}
97633              * @private
97634              */
97635             this.hashSet_ = hashSet;
97636             /**
97637              * @type {number}
97638              * @private
97639              */
97640             this.position_ = 0;
97641           }
97642
97643           if ( Iterator$$1 ) { Iterator_.__proto__ = Iterator$$1; }
97644           Iterator_.prototype = Object.create( Iterator$$1 && Iterator$$1.prototype );
97645           Iterator_.prototype.constructor = Iterator_;
97646
97647           /**
97648            * @override
97649            */
97650           Iterator_.prototype.next = function next () {
97651             if (this.position_ === this.hashSet_.size()) {
97652               throw new NoSuchElementException()
97653             }
97654             return this.hashSet_.array_[this.position_++]
97655           };
97656
97657           /**
97658            * @override
97659            */
97660           Iterator_.prototype.hasNext = function hasNext () {
97661             if (this.position_ < this.hashSet_.size()) {
97662               return true
97663             } else {
97664               return false
97665             }
97666           };
97667
97668           /**
97669            * @override
97670            */
97671           Iterator_.prototype.remove = function remove () {
97672             throw new OperationNotSupported()
97673           };
97674
97675           return Iterator_;
97676         }(Iterator$1));
97677
97678         var BLACK = 0;
97679         var RED = 1;
97680         function colorOf (p) { return (p === null ? BLACK : p.color) }
97681         function parentOf (p) { return (p === null ? null : p.parent) }
97682         function setColor (p, c) { if (p !== null) { p.color = c; } }
97683         function leftOf (p) { return (p === null ? null : p.left) }
97684         function rightOf (p) { return (p === null ? null : p.right) }
97685
97686         /**
97687          * @see http://download.oracle.com/javase/6/docs/api/java/util/TreeMap.html
97688          *
97689          * @extends {SortedMap}
97690          * @constructor
97691          * @private
97692          */
97693         function TreeMap () {
97694           /**
97695            * @type {Object}
97696            * @private
97697            */
97698           this.root_ = null;
97699           /**
97700            * @type {number}
97701            * @private
97702           */
97703           this.size_ = 0;
97704         }
97705         TreeMap.prototype = new SortedMap();
97706
97707         /**
97708          * @override
97709          */
97710         TreeMap.prototype.get = function (key) {
97711           var p = this.root_;
97712           while (p !== null) {
97713             var cmp = key['compareTo'](p.key);
97714             if (cmp < 0) { p = p.left; }
97715             else if (cmp > 0) { p = p.right; }
97716             else { return p.value }
97717           }
97718           return null
97719         };
97720
97721         /**
97722          * @override
97723          */
97724         TreeMap.prototype.put = function (key, value) {
97725           if (this.root_ === null) {
97726             this.root_ = {
97727               key: key,
97728               value: value,
97729               left: null,
97730               right: null,
97731               parent: null,
97732               color: BLACK,
97733               getValue: function getValue () { return this.value },
97734               getKey: function getKey () { return this.key }
97735             };
97736             this.size_ = 1;
97737             return null
97738           }
97739           var t = this.root_;
97740           var parent;
97741           var cmp;
97742           do {
97743             parent = t;
97744             cmp = key['compareTo'](t.key);
97745             if (cmp < 0) {
97746               t = t.left;
97747             } else if (cmp > 0) {
97748               t = t.right;
97749             } else {
97750               var oldValue = t.value;
97751               t.value = value;
97752               return oldValue
97753             }
97754           } while (t !== null)
97755           var e = {
97756             key: key,
97757             left: null,
97758             right: null,
97759             value: value,
97760             parent: parent,
97761             color: BLACK,
97762             getValue: function getValue () { return this.value },
97763             getKey: function getKey () { return this.key }
97764           };
97765           if (cmp < 0) {
97766             parent.left = e;
97767           } else {
97768             parent.right = e;
97769           }
97770           this.fixAfterInsertion(e);
97771           this.size_++;
97772           return null
97773         };
97774
97775         /**
97776          * @param {Object} x
97777          */
97778         TreeMap.prototype.fixAfterInsertion = function (x) {
97779           var this$1 = this;
97780
97781           x.color = RED;
97782           while (x != null && x !== this.root_ && x.parent.color === RED) {
97783             if (parentOf(x) === leftOf(parentOf(parentOf(x)))) {
97784               var y = rightOf(parentOf(parentOf(x)));
97785               if (colorOf(y) === RED) {
97786                 setColor(parentOf(x), BLACK);
97787                 setColor(y, BLACK);
97788                 setColor(parentOf(parentOf(x)), RED);
97789                 x = parentOf(parentOf(x));
97790               } else {
97791                 if (x === rightOf(parentOf(x))) {
97792                   x = parentOf(x);
97793                   this$1.rotateLeft(x);
97794                 }
97795                 setColor(parentOf(x), BLACK);
97796                 setColor(parentOf(parentOf(x)), RED);
97797                 this$1.rotateRight(parentOf(parentOf(x)));
97798               }
97799             } else {
97800               var y$1 = leftOf(parentOf(parentOf(x)));
97801               if (colorOf(y$1) === RED) {
97802                 setColor(parentOf(x), BLACK);
97803                 setColor(y$1, BLACK);
97804                 setColor(parentOf(parentOf(x)), RED);
97805                 x = parentOf(parentOf(x));
97806               } else {
97807                 if (x === leftOf(parentOf(x))) {
97808                   x = parentOf(x);
97809                   this$1.rotateRight(x);
97810                 }
97811                 setColor(parentOf(x), BLACK);
97812                 setColor(parentOf(parentOf(x)), RED);
97813                 this$1.rotateLeft(parentOf(parentOf(x)));
97814               }
97815             }
97816           }
97817           this.root_.color = BLACK;
97818         };
97819
97820         /**
97821          * @override
97822          */
97823         TreeMap.prototype.values = function () {
97824           var arrayList = new ArrayList();
97825           var p = this.getFirstEntry();
97826           if (p !== null) {
97827             arrayList.add(p.value);
97828             while ((p = TreeMap.successor(p)) !== null) {
97829               arrayList.add(p.value);
97830             }
97831           }
97832           return arrayList
97833         };
97834
97835         /**
97836          * @override
97837          */
97838         TreeMap.prototype.entrySet = function () {
97839           var hashSet = new HashSet();
97840           var p = this.getFirstEntry();
97841           if (p !== null) {
97842             hashSet.add(p);
97843             while ((p = TreeMap.successor(p)) !== null) {
97844               hashSet.add(p);
97845             }
97846           }
97847           return hashSet
97848         };
97849
97850         /**
97851          * @param {Object} p
97852          */
97853         TreeMap.prototype.rotateLeft = function (p) {
97854           if (p != null) {
97855             var r = p.right;
97856             p.right = r.left;
97857             if (r.left != null) { r.left.parent = p; }
97858             r.parent = p.parent;
97859             if (p.parent === null) { this.root_ = r; } else if (p.parent.left === p) { p.parent.left = r; } else { p.parent.right = r; }
97860             r.left = p;
97861             p.parent = r;
97862           }
97863         };
97864
97865         /**
97866          * @param {Object} p
97867          */
97868         TreeMap.prototype.rotateRight = function (p) {
97869           if (p != null) {
97870             var l = p.left;
97871             p.left = l.right;
97872             if (l.right != null) { l.right.parent = p; }
97873             l.parent = p.parent;
97874             if (p.parent === null) { this.root_ = l; } else if (p.parent.right === p) { p.parent.right = l; } else { p.parent.left = l; }
97875             l.right = p;
97876             p.parent = l;
97877           }
97878         };
97879
97880         /**
97881          * @return {Object}
97882          */
97883         TreeMap.prototype.getFirstEntry = function () {
97884           var p = this.root_;
97885           if (p != null) {
97886             while (p.left != null) {
97887               p = p.left;
97888             }
97889           }
97890           return p
97891         };
97892
97893         /**
97894          * @param {Object} t
97895          * @return {Object}
97896          * @private
97897          */
97898         TreeMap.successor = function (t) {
97899           if (t === null) { return null } else if (t.right !== null) {
97900             var p = t.right;
97901             while (p.left !== null) {
97902               p = p.left;
97903             }
97904             return p
97905           } else {
97906             var p$1 = t.parent;
97907             var ch = t;
97908             while (p$1 !== null && ch === p$1.right) {
97909               ch = p$1;
97910               p$1 = p$1.parent;
97911             }
97912             return p$1
97913           }
97914         };
97915
97916         /**
97917          * @override
97918          */
97919         TreeMap.prototype.size = function () {
97920           return this.size_
97921         };
97922
97923         var Lineal = function Lineal () {};
97924
97925         Lineal.prototype.interfaces_ = function interfaces_ () {
97926           return []
97927         };
97928         Lineal.prototype.getClass = function getClass () {
97929           return Lineal
97930         };
97931
97932         /**
97933          * @see http://download.oracle.com/javase/6/docs/api/java/util/SortedSet.html
97934          *
97935          * @extends {Set}
97936          * @constructor
97937          * @private
97938          */
97939         function SortedSet () {}
97940         SortedSet.prototype = new Set$2();
97941
97942         // import Iterator from './Iterator'
97943         /**
97944          * @see http://download.oracle.com/javase/6/docs/api/java/util/TreeSet.html
97945          *
97946          * @extends {SortedSet}
97947          * @constructor
97948          * @private
97949          */
97950         function TreeSet () {
97951           /**
97952            * @type {Array}
97953            * @private
97954           */
97955           this.array_ = [];
97956
97957           if (arguments[0] instanceof Collection) {
97958             this.addAll(arguments[0]);
97959           }
97960         }
97961         TreeSet.prototype = new SortedSet();
97962
97963         /**
97964          * @override
97965          */
97966         TreeSet.prototype.contains = function (o) {
97967           var this$1 = this;
97968
97969           for (var i = 0, len = this.array_.length; i < len; i++) {
97970             var e = this$1.array_[i];
97971             if (e['compareTo'](o) === 0) {
97972               return true
97973             }
97974           }
97975           return false
97976         };
97977
97978         /**
97979          * @override
97980          */
97981         TreeSet.prototype.add = function (o) {
97982           var this$1 = this;
97983
97984           if (this.contains(o)) {
97985             return false
97986           }
97987
97988           for (var i = 0, len = this.array_.length; i < len; i++) {
97989             var e = this$1.array_[i];
97990             if (e['compareTo'](o) === 1) {
97991               this$1.array_.splice(i, 0, o);
97992               return true
97993             }
97994           }
97995
97996           this.array_.push(o);
97997
97998           return true
97999         };
98000
98001         /**
98002          * @override
98003          */
98004         TreeSet.prototype.addAll = function (c) {
98005           var this$1 = this;
98006
98007           for (var i = c.iterator(); i.hasNext();) {
98008             this$1.add(i.next());
98009           }
98010           return true
98011         };
98012
98013         /**
98014          * @override
98015          */
98016         TreeSet.prototype.remove = function (e) {
98017           throw new OperationNotSupported()
98018         };
98019
98020         /**
98021          * @override
98022          */
98023         TreeSet.prototype.size = function () {
98024           return this.array_.length
98025         };
98026
98027         /**
98028          * @override
98029          */
98030         TreeSet.prototype.isEmpty = function () {
98031           return this.array_.length === 0
98032         };
98033
98034         /**
98035          * @override
98036          */
98037         TreeSet.prototype.toArray = function () {
98038           var this$1 = this;
98039
98040           var array = [];
98041
98042           for (var i = 0, len = this.array_.length; i < len; i++) {
98043             array.push(this$1.array_[i]);
98044           }
98045
98046           return array
98047         };
98048
98049         /**
98050          * @override
98051          */
98052         TreeSet.prototype.iterator = function () {
98053           return new Iterator_$2(this)
98054         };
98055
98056         /**
98057          * @extends {javascript.util.Iterator}
98058          * @param {javascript.util.TreeSet} treeSet
98059          * @constructor
98060          * @private
98061          */
98062         var Iterator_$2 = function (treeSet) {
98063           /**
98064            * @type {javascript.util.TreeSet}
98065            * @private
98066            */
98067           this.treeSet_ = treeSet;
98068           /**
98069            * @type {number}
98070            * @private
98071            */
98072           this.position_ = 0;
98073         };
98074
98075         /**
98076          * @override
98077          */
98078         Iterator_$2.prototype.next = function () {
98079           if (this.position_ === this.treeSet_.size()) {
98080             throw new NoSuchElementException()
98081           }
98082           return this.treeSet_.array_[this.position_++]
98083         };
98084
98085         /**
98086          * @override
98087          */
98088         Iterator_$2.prototype.hasNext = function () {
98089           if (this.position_ < this.treeSet_.size()) {
98090             return true
98091           } else {
98092             return false
98093           }
98094         };
98095
98096         /**
98097          * @override
98098          */
98099         Iterator_$2.prototype.remove = function () {
98100           throw new OperationNotSupported()
98101         };
98102
98103         /**
98104          * @see http://download.oracle.com/javase/6/docs/api/java/util/Arrays.html
98105          *
98106          * @constructor
98107          * @private
98108          */
98109         var Arrays = function Arrays () {};
98110
98111         Arrays.sort = function sort () {
98112           var a = arguments[0];
98113           var i;
98114           var t;
98115           var comparator;
98116           var compare;
98117           if (arguments.length === 1) {
98118             compare = function (a, b) {
98119               return a.compareTo(b)
98120             };
98121             a.sort(compare);
98122           } else if (arguments.length === 2) {
98123             comparator = arguments[1];
98124             compare = function (a, b) {
98125               return comparator['compare'](a, b)
98126             };
98127             a.sort(compare);
98128           } else if (arguments.length === 3) {
98129             t = a.slice(arguments[1], arguments[2]);
98130             t.sort();
98131             var r = a.slice(0, arguments[1]).concat(t, a.slice(arguments[2], a.length));
98132             a.splice(0, a.length);
98133             for (i = 0; i < r.length; i++) {
98134               a.push(r[i]);
98135             }
98136           } else if (arguments.length === 4) {
98137             t = a.slice(arguments[1], arguments[2]);
98138             comparator = arguments[3];
98139             compare = function (a, b) {
98140               return comparator['compare'](a, b)
98141             };
98142             t.sort(compare);
98143             r = a.slice(0, arguments[1]).concat(t, a.slice(arguments[2], a.length));
98144             a.splice(0, a.length);
98145             for (i = 0; i < r.length; i++) {
98146               a.push(r[i]);
98147             }
98148           }
98149         };
98150         /**
98151          * @param {Array} array
98152          * @return {ArrayList}
98153          */
98154         Arrays.asList = function asList (array) {
98155           var arrayList = new ArrayList();
98156           for (var i = 0, len = array.length; i < len; i++) {
98157             arrayList.add(array[i]);
98158           }
98159           return arrayList
98160         };
98161
98162         var Dimension = function Dimension () {};
98163
98164         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 } };
98165
98166         staticAccessors$14.P.get = function () { return 0 };
98167         staticAccessors$14.L.get = function () { return 1 };
98168         staticAccessors$14.A.get = function () { return 2 };
98169         staticAccessors$14.FALSE.get = function () { return -1 };
98170         staticAccessors$14.TRUE.get = function () { return -2 };
98171         staticAccessors$14.DONTCARE.get = function () { return -3 };
98172         staticAccessors$14.SYM_FALSE.get = function () { return 'F' };
98173         staticAccessors$14.SYM_TRUE.get = function () { return 'T' };
98174         staticAccessors$14.SYM_DONTCARE.get = function () { return '*' };
98175         staticAccessors$14.SYM_P.get = function () { return '0' };
98176         staticAccessors$14.SYM_L.get = function () { return '1' };
98177         staticAccessors$14.SYM_A.get = function () { return '2' };
98178
98179         Dimension.prototype.interfaces_ = function interfaces_ () {
98180           return []
98181         };
98182         Dimension.prototype.getClass = function getClass () {
98183           return Dimension
98184         };
98185         Dimension.toDimensionSymbol = function toDimensionSymbol (dimensionValue) {
98186           switch (dimensionValue) {
98187             case Dimension.FALSE:
98188               return Dimension.SYM_FALSE
98189             case Dimension.TRUE:
98190               return Dimension.SYM_TRUE
98191             case Dimension.DONTCARE:
98192               return Dimension.SYM_DONTCARE
98193             case Dimension.P:
98194               return Dimension.SYM_P
98195             case Dimension.L:
98196               return Dimension.SYM_L
98197             case Dimension.A:
98198               return Dimension.SYM_A
98199           }
98200           throw new IllegalArgumentException('Unknown dimension value: ' + dimensionValue)
98201         };
98202         Dimension.toDimensionValue = function toDimensionValue (dimensionSymbol) {
98203           switch (Character.toUpperCase(dimensionSymbol)) {
98204             case Dimension.SYM_FALSE:
98205               return Dimension.FALSE
98206             case Dimension.SYM_TRUE:
98207               return Dimension.TRUE
98208             case Dimension.SYM_DONTCARE:
98209               return Dimension.DONTCARE
98210             case Dimension.SYM_P:
98211               return Dimension.P
98212             case Dimension.SYM_L:
98213               return Dimension.L
98214             case Dimension.SYM_A:
98215               return Dimension.A
98216           }
98217           throw new IllegalArgumentException('Unknown dimension symbol: ' + dimensionSymbol)
98218         };
98219
98220         Object.defineProperties( Dimension, staticAccessors$14 );
98221
98222         var GeometryFilter = function GeometryFilter () {};
98223
98224         GeometryFilter.prototype.filter = function filter (geom) {};
98225         GeometryFilter.prototype.interfaces_ = function interfaces_ () {
98226           return []
98227         };
98228         GeometryFilter.prototype.getClass = function getClass () {
98229           return GeometryFilter
98230         };
98231
98232         var CoordinateSequenceFilter = function CoordinateSequenceFilter () {};
98233
98234         CoordinateSequenceFilter.prototype.filter = function filter (seq, i) {};
98235         CoordinateSequenceFilter.prototype.isDone = function isDone () {};
98236         CoordinateSequenceFilter.prototype.isGeometryChanged = function isGeometryChanged () {};
98237         CoordinateSequenceFilter.prototype.interfaces_ = function interfaces_ () {
98238           return []
98239         };
98240         CoordinateSequenceFilter.prototype.getClass = function getClass () {
98241           return CoordinateSequenceFilter
98242         };
98243
98244         var GeometryCollection = (function (Geometry$$1) {
98245           function GeometryCollection (geometries, factory) {
98246             Geometry$$1.call(this, factory);
98247             this._geometries = geometries || [];
98248
98249             if (Geometry$$1.hasNullElements(this._geometries)) {
98250               throw new IllegalArgumentException('geometries must not contain null elements')
98251             }
98252           }
98253
98254           if ( Geometry$$1 ) { GeometryCollection.__proto__ = Geometry$$1; }
98255           GeometryCollection.prototype = Object.create( Geometry$$1 && Geometry$$1.prototype );
98256           GeometryCollection.prototype.constructor = GeometryCollection;
98257
98258           var staticAccessors = { serialVersionUID: { configurable: true } };
98259           GeometryCollection.prototype.computeEnvelopeInternal = function computeEnvelopeInternal () {
98260             var this$1 = this;
98261
98262             var envelope = new Envelope();
98263             for (var i = 0; i < this._geometries.length; i++) {
98264               envelope.expandToInclude(this$1._geometries[i].getEnvelopeInternal());
98265             }
98266             return envelope
98267           };
98268           GeometryCollection.prototype.getGeometryN = function getGeometryN (n) {
98269             return this._geometries[n]
98270           };
98271           GeometryCollection.prototype.getSortIndex = function getSortIndex () {
98272             return Geometry$$1.SORTINDEX_GEOMETRYCOLLECTION
98273           };
98274           GeometryCollection.prototype.getCoordinates = function getCoordinates () {
98275             var this$1 = this;
98276
98277             var coordinates = new Array(this.getNumPoints()).fill(null);
98278             var k = -1;
98279             for (var i = 0; i < this._geometries.length; i++) {
98280               var childCoordinates = this$1._geometries[i].getCoordinates();
98281               for (var j = 0; j < childCoordinates.length; j++) {
98282                 k++;
98283                 coordinates[k] = childCoordinates[j];
98284               }
98285             }
98286             return coordinates
98287           };
98288           GeometryCollection.prototype.getArea = function getArea () {
98289             var this$1 = this;
98290
98291             var area = 0.0;
98292             for (var i = 0; i < this._geometries.length; i++) {
98293               area += this$1._geometries[i].getArea();
98294             }
98295             return area
98296           };
98297           GeometryCollection.prototype.equalsExact = function equalsExact () {
98298             var this$1 = this;
98299
98300             if (arguments.length === 2) {
98301               var other = arguments[0];
98302               var tolerance = arguments[1];
98303               if (!this.isEquivalentClass(other)) {
98304                 return false
98305               }
98306               var otherCollection = other;
98307               if (this._geometries.length !== otherCollection._geometries.length) {
98308                 return false
98309               }
98310               for (var i = 0; i < this._geometries.length; i++) {
98311                 if (!this$1._geometries[i].equalsExact(otherCollection._geometries[i], tolerance)) {
98312                   return false
98313                 }
98314               }
98315               return true
98316             } else { return Geometry$$1.prototype.equalsExact.apply(this, arguments) }
98317           };
98318           GeometryCollection.prototype.normalize = function normalize () {
98319             var this$1 = this;
98320
98321             for (var i = 0; i < this._geometries.length; i++) {
98322               this$1._geometries[i].normalize();
98323             }
98324             Arrays.sort(this._geometries);
98325           };
98326           GeometryCollection.prototype.getCoordinate = function getCoordinate () {
98327             if (this.isEmpty()) { return null }
98328             return this._geometries[0].getCoordinate()
98329           };
98330           GeometryCollection.prototype.getBoundaryDimension = function getBoundaryDimension () {
98331             var this$1 = this;
98332
98333             var dimension = Dimension.FALSE;
98334             for (var i = 0; i < this._geometries.length; i++) {
98335               dimension = Math.max(dimension, this$1._geometries[i].getBoundaryDimension());
98336             }
98337             return dimension
98338           };
98339           GeometryCollection.prototype.getDimension = function getDimension () {
98340             var this$1 = this;
98341
98342             var dimension = Dimension.FALSE;
98343             for (var i = 0; i < this._geometries.length; i++) {
98344               dimension = Math.max(dimension, this$1._geometries[i].getDimension());
98345             }
98346             return dimension
98347           };
98348           GeometryCollection.prototype.getLength = function getLength () {
98349             var this$1 = this;
98350
98351             var sum = 0.0;
98352             for (var i = 0; i < this._geometries.length; i++) {
98353               sum += this$1._geometries[i].getLength();
98354             }
98355             return sum
98356           };
98357           GeometryCollection.prototype.getNumPoints = function getNumPoints () {
98358             var this$1 = this;
98359
98360             var numPoints = 0;
98361             for (var i = 0; i < this._geometries.length; i++) {
98362               numPoints += this$1._geometries[i].getNumPoints();
98363             }
98364             return numPoints
98365           };
98366           GeometryCollection.prototype.getNumGeometries = function getNumGeometries () {
98367             return this._geometries.length
98368           };
98369           GeometryCollection.prototype.reverse = function reverse () {
98370             var this$1 = this;
98371
98372             var n = this._geometries.length;
98373             var revGeoms = new Array(n).fill(null);
98374             for (var i = 0; i < this._geometries.length; i++) {
98375               revGeoms[i] = this$1._geometries[i].reverse();
98376             }
98377             return this.getFactory().createGeometryCollection(revGeoms)
98378           };
98379           GeometryCollection.prototype.compareToSameClass = function compareToSameClass () {
98380             var this$1 = this;
98381
98382             if (arguments.length === 1) {
98383               var o = arguments[0];
98384               var theseElements = new TreeSet(Arrays.asList(this._geometries));
98385               var otherElements = new TreeSet(Arrays.asList(o._geometries));
98386               return this.compare(theseElements, otherElements)
98387             } else if (arguments.length === 2) {
98388               var o$1 = arguments[0];
98389               var comp = arguments[1];
98390               var gc = o$1;
98391               var n1 = this.getNumGeometries();
98392               var n2 = gc.getNumGeometries();
98393               var i = 0;
98394               while (i < n1 && i < n2) {
98395                 var thisGeom = this$1.getGeometryN(i);
98396                 var otherGeom = gc.getGeometryN(i);
98397                 var holeComp = thisGeom.compareToSameClass(otherGeom, comp);
98398                 if (holeComp !== 0) { return holeComp }
98399                 i++;
98400               }
98401               if (i < n1) { return 1 }
98402               if (i < n2) { return -1 }
98403               return 0
98404             }
98405           };
98406           GeometryCollection.prototype.apply = function apply () {
98407             var this$1 = this;
98408
98409             if (hasInterface(arguments[0], CoordinateFilter)) {
98410               var filter = arguments[0];
98411               for (var i = 0; i < this._geometries.length; i++) {
98412                 this$1._geometries[i].apply(filter);
98413               }
98414             } else if (hasInterface(arguments[0], CoordinateSequenceFilter)) {
98415               var filter$1 = arguments[0];
98416               if (this._geometries.length === 0) { return null }
98417               for (var i$1 = 0; i$1 < this._geometries.length; i$1++) {
98418                 this$1._geometries[i$1].apply(filter$1);
98419                 if (filter$1.isDone()) {
98420                   break
98421                 }
98422               }
98423               if (filter$1.isGeometryChanged()) { this.geometryChanged(); }
98424             } else if (hasInterface(arguments[0], GeometryFilter)) {
98425               var filter$2 = arguments[0];
98426               filter$2.filter(this);
98427               for (var i$2 = 0; i$2 < this._geometries.length; i$2++) {
98428                 this$1._geometries[i$2].apply(filter$2);
98429               }
98430             } else if (hasInterface(arguments[0], GeometryComponentFilter)) {
98431               var filter$3 = arguments[0];
98432               filter$3.filter(this);
98433               for (var i$3 = 0; i$3 < this._geometries.length; i$3++) {
98434                 this$1._geometries[i$3].apply(filter$3);
98435               }
98436             }
98437           };
98438           GeometryCollection.prototype.getBoundary = function getBoundary () {
98439             this.checkNotGeometryCollection(this);
98440             Assert.shouldNeverReachHere();
98441             return null
98442           };
98443           GeometryCollection.prototype.clone = function clone () {
98444             var this$1 = this;
98445
98446             var gc = Geometry$$1.prototype.clone.call(this);
98447             gc._geometries = new Array(this._geometries.length).fill(null);
98448             for (var i = 0; i < this._geometries.length; i++) {
98449               gc._geometries[i] = this$1._geometries[i].clone();
98450             }
98451             return gc
98452           };
98453           GeometryCollection.prototype.getGeometryType = function getGeometryType () {
98454             return 'GeometryCollection'
98455           };
98456           GeometryCollection.prototype.copy = function copy () {
98457             var this$1 = this;
98458
98459             var geometries = new Array(this._geometries.length).fill(null);
98460             for (var i = 0; i < geometries.length; i++) {
98461               geometries[i] = this$1._geometries[i].copy();
98462             }
98463             return new GeometryCollection(geometries, this._factory)
98464           };
98465           GeometryCollection.prototype.isEmpty = function isEmpty () {
98466             var this$1 = this;
98467
98468             for (var i = 0; i < this._geometries.length; i++) {
98469               if (!this$1._geometries[i].isEmpty()) {
98470                 return false
98471               }
98472             }
98473             return true
98474           };
98475           GeometryCollection.prototype.interfaces_ = function interfaces_ () {
98476             return []
98477           };
98478           GeometryCollection.prototype.getClass = function getClass () {
98479             return GeometryCollection
98480           };
98481           staticAccessors.serialVersionUID.get = function () { return -5694727726395021467 };
98482
98483           Object.defineProperties( GeometryCollection, staticAccessors );
98484
98485           return GeometryCollection;
98486         }(Geometry));
98487
98488         var MultiLineString = (function (GeometryCollection$$1) {
98489           function MultiLineString () {
98490             GeometryCollection$$1.apply(this, arguments);
98491           }
98492
98493           if ( GeometryCollection$$1 ) { MultiLineString.__proto__ = GeometryCollection$$1; }
98494           MultiLineString.prototype = Object.create( GeometryCollection$$1 && GeometryCollection$$1.prototype );
98495           MultiLineString.prototype.constructor = MultiLineString;
98496
98497           var staticAccessors = { serialVersionUID: { configurable: true } };
98498
98499           MultiLineString.prototype.getSortIndex = function getSortIndex () {
98500             return Geometry.SORTINDEX_MULTILINESTRING
98501           };
98502           MultiLineString.prototype.equalsExact = function equalsExact () {
98503             if (arguments.length === 2) {
98504               var other = arguments[0];
98505               var tolerance = arguments[1];
98506               if (!this.isEquivalentClass(other)) {
98507                 return false
98508               }
98509               return GeometryCollection$$1.prototype.equalsExact.call(this, other, tolerance)
98510             } else { return GeometryCollection$$1.prototype.equalsExact.apply(this, arguments) }
98511           };
98512           MultiLineString.prototype.getBoundaryDimension = function getBoundaryDimension () {
98513             if (this.isClosed()) {
98514               return Dimension.FALSE
98515             }
98516             return 0
98517           };
98518           MultiLineString.prototype.isClosed = function isClosed () {
98519             var this$1 = this;
98520
98521             if (this.isEmpty()) {
98522               return false
98523             }
98524             for (var i = 0; i < this._geometries.length; i++) {
98525               if (!this$1._geometries[i].isClosed()) {
98526                 return false
98527               }
98528             }
98529             return true
98530           };
98531           MultiLineString.prototype.getDimension = function getDimension () {
98532             return 1
98533           };
98534           MultiLineString.prototype.reverse = function reverse () {
98535             var this$1 = this;
98536
98537             var nLines = this._geometries.length;
98538             var revLines = new Array(nLines).fill(null);
98539             for (var i = 0; i < this._geometries.length; i++) {
98540               revLines[nLines - 1 - i] = this$1._geometries[i].reverse();
98541             }
98542             return this.getFactory().createMultiLineString(revLines)
98543           };
98544           MultiLineString.prototype.getBoundary = function getBoundary () {
98545             return new BoundaryOp(this).getBoundary()
98546           };
98547           MultiLineString.prototype.getGeometryType = function getGeometryType () {
98548             return 'MultiLineString'
98549           };
98550           MultiLineString.prototype.copy = function copy () {
98551             var this$1 = this;
98552
98553             var lineStrings = new Array(this._geometries.length).fill(null);
98554             for (var i = 0; i < lineStrings.length; i++) {
98555               lineStrings[i] = this$1._geometries[i].copy();
98556             }
98557             return new MultiLineString(lineStrings, this._factory)
98558           };
98559           MultiLineString.prototype.interfaces_ = function interfaces_ () {
98560             return [Lineal]
98561           };
98562           MultiLineString.prototype.getClass = function getClass () {
98563             return MultiLineString
98564           };
98565           staticAccessors.serialVersionUID.get = function () { return 8166665132445433741 };
98566
98567           Object.defineProperties( MultiLineString, staticAccessors );
98568
98569           return MultiLineString;
98570         }(GeometryCollection));
98571
98572         var BoundaryOp = function BoundaryOp () {
98573           this._geom = null;
98574           this._geomFact = null;
98575           this._bnRule = null;
98576           this._endpointMap = null;
98577           if (arguments.length === 1) {
98578             var geom = arguments[0];
98579             var bnRule = BoundaryNodeRule.MOD2_BOUNDARY_RULE;
98580             this._geom = geom;
98581             this._geomFact = geom.getFactory();
98582             this._bnRule = bnRule;
98583           } else if (arguments.length === 2) {
98584             var geom$1 = arguments[0];
98585             var bnRule$1 = arguments[1];
98586             this._geom = geom$1;
98587             this._geomFact = geom$1.getFactory();
98588             this._bnRule = bnRule$1;
98589           }
98590         };
98591         BoundaryOp.prototype.boundaryMultiLineString = function boundaryMultiLineString (mLine) {
98592           if (this._geom.isEmpty()) {
98593             return this.getEmptyMultiPoint()
98594           }
98595           var bdyPts = this.computeBoundaryCoordinates(mLine);
98596           if (bdyPts.length === 1) {
98597             return this._geomFact.createPoint(bdyPts[0])
98598           }
98599           return this._geomFact.createMultiPointFromCoords(bdyPts)
98600         };
98601         BoundaryOp.prototype.getBoundary = function getBoundary () {
98602           if (this._geom instanceof LineString) { return this.boundaryLineString(this._geom) }
98603           if (this._geom instanceof MultiLineString) { return this.boundaryMultiLineString(this._geom) }
98604           return this._geom.getBoundary()
98605         };
98606         BoundaryOp.prototype.boundaryLineString = function boundaryLineString (line) {
98607           if (this._geom.isEmpty()) {
98608             return this.getEmptyMultiPoint()
98609           }
98610           if (line.isClosed()) {
98611             var closedEndpointOnBoundary = this._bnRule.isInBoundary(2);
98612             if (closedEndpointOnBoundary) {
98613               return line.getStartPoint()
98614             } else {
98615               return this._geomFact.createMultiPoint()
98616             }
98617           }
98618           return this._geomFact.createMultiPoint([line.getStartPoint(), line.getEndPoint()])
98619         };
98620         BoundaryOp.prototype.getEmptyMultiPoint = function getEmptyMultiPoint () {
98621           return this._geomFact.createMultiPoint()
98622         };
98623         BoundaryOp.prototype.computeBoundaryCoordinates = function computeBoundaryCoordinates (mLine) {
98624             var this$1 = this;
98625
98626           var bdyPts = new ArrayList();
98627           this._endpointMap = new TreeMap();
98628           for (var i = 0; i < mLine.getNumGeometries(); i++) {
98629             var line = mLine.getGeometryN(i);
98630             if (line.getNumPoints() === 0) { continue }
98631             this$1.addEndpoint(line.getCoordinateN(0));
98632             this$1.addEndpoint(line.getCoordinateN(line.getNumPoints() - 1));
98633           }
98634           for (var it = this._endpointMap.entrySet().iterator(); it.hasNext();) {
98635             var entry = it.next();
98636             var counter = entry.getValue();
98637             var valence = counter.count;
98638             if (this$1._bnRule.isInBoundary(valence)) {
98639               bdyPts.add(entry.getKey());
98640             }
98641           }
98642           return CoordinateArrays.toCoordinateArray(bdyPts)
98643         };
98644         BoundaryOp.prototype.addEndpoint = function addEndpoint (pt) {
98645           var counter = this._endpointMap.get(pt);
98646           if (counter === null) {
98647             counter = new Counter();
98648             this._endpointMap.put(pt, counter);
98649           }
98650           counter.count++;
98651         };
98652         BoundaryOp.prototype.interfaces_ = function interfaces_ () {
98653           return []
98654         };
98655         BoundaryOp.prototype.getClass = function getClass () {
98656           return BoundaryOp
98657         };
98658         BoundaryOp.getBoundary = function getBoundary () {
98659           if (arguments.length === 1) {
98660             var g = arguments[0];
98661             var bop = new BoundaryOp(g);
98662             return bop.getBoundary()
98663           } else if (arguments.length === 2) {
98664             var g$1 = arguments[0];
98665             var bnRule = arguments[1];
98666             var bop$1 = new BoundaryOp(g$1, bnRule);
98667             return bop$1.getBoundary()
98668           }
98669         };
98670
98671         var Counter = function Counter () {
98672           this.count = null;
98673         };
98674         Counter.prototype.interfaces_ = function interfaces_ () {
98675           return []
98676         };
98677         Counter.prototype.getClass = function getClass () {
98678           return Counter
98679         };
98680
98681         // boundary
98682
98683         function PrintStream () {}
98684
98685         function StringReader () {}
98686
98687         var DecimalFormat = function DecimalFormat () {};
98688
98689         function ByteArrayOutputStream () {}
98690
98691         function IOException () {}
98692
98693         function LineNumberReader () {}
98694
98695         var StringUtil = function StringUtil () {};
98696
98697         var staticAccessors$15 = { NEWLINE: { configurable: true },SIMPLE_ORDINATE_FORMAT: { configurable: true } };
98698
98699         StringUtil.prototype.interfaces_ = function interfaces_ () {
98700           return []
98701         };
98702         StringUtil.prototype.getClass = function getClass () {
98703           return StringUtil
98704         };
98705         StringUtil.chars = function chars (c, n) {
98706           var ch = new Array(n).fill(null);
98707           for (var i = 0; i < n; i++) {
98708             ch[i] = c;
98709           }
98710           return String(ch)
98711         };
98712         StringUtil.getStackTrace = function getStackTrace () {
98713           if (arguments.length === 1) {
98714             var t = arguments[0];
98715             var os = new ByteArrayOutputStream();
98716             var ps = new PrintStream(os);
98717             t.printStackTrace(ps);
98718             return os.toString()
98719           } else if (arguments.length === 2) {
98720             var t$1 = arguments[0];
98721             var depth = arguments[1];
98722             var stackTrace = '';
98723             var stringReader = new StringReader(StringUtil.getStackTrace(t$1));
98724             var lineNumberReader = new LineNumberReader(stringReader);
98725             for (var i = 0; i < depth; i++) {
98726               try {
98727                 stackTrace += lineNumberReader.readLine() + StringUtil.NEWLINE;
98728               } catch (e) {
98729                 if (e instanceof IOException) {
98730                   Assert.shouldNeverReachHere();
98731                 } else { throw e }
98732               } finally {}
98733             }
98734             return stackTrace
98735           }
98736         };
98737         StringUtil.split = function split (s, separator) {
98738           var separatorlen = separator.length;
98739           var tokenList = new ArrayList();
98740           var tmpString = '' + s;
98741           var pos = tmpString.indexOf(separator);
98742           while (pos >= 0) {
98743             var token = tmpString.substring(0, pos);
98744             tokenList.add(token);
98745             tmpString = tmpString.substring(pos + separatorlen);
98746             pos = tmpString.indexOf(separator);
98747           }
98748           if (tmpString.length > 0) { tokenList.add(tmpString); }
98749           var res = new Array(tokenList.size()).fill(null);
98750           for (var i = 0; i < res.length; i++) {
98751             res[i] = tokenList.get(i);
98752           }
98753           return res
98754         };
98755         StringUtil.toString = function toString () {
98756           if (arguments.length === 1) {
98757             var d = arguments[0];
98758             return StringUtil.SIMPLE_ORDINATE_FORMAT.format(d)
98759           }
98760         };
98761         StringUtil.spaces = function spaces (n) {
98762           return StringUtil.chars(' ', n)
98763         };
98764         staticAccessors$15.NEWLINE.get = function () { return System.getProperty('line.separator') };
98765         staticAccessors$15.SIMPLE_ORDINATE_FORMAT.get = function () { return new DecimalFormat('0.#') };
98766
98767         Object.defineProperties( StringUtil, staticAccessors$15 );
98768
98769         var CoordinateSequences = function CoordinateSequences () {};
98770
98771         CoordinateSequences.prototype.interfaces_ = function interfaces_ () {
98772           return []
98773         };
98774         CoordinateSequences.prototype.getClass = function getClass () {
98775           return CoordinateSequences
98776         };
98777         CoordinateSequences.copyCoord = function copyCoord (src, srcPos, dest, destPos) {
98778           var minDim = Math.min(src.getDimension(), dest.getDimension());
98779           for (var dim = 0; dim < minDim; dim++) {
98780             dest.setOrdinate(destPos, dim, src.getOrdinate(srcPos, dim));
98781           }
98782         };
98783         CoordinateSequences.isRing = function isRing (seq) {
98784           var n = seq.size();
98785           if (n === 0) { return true }
98786           if (n <= 3) { return false }
98787           return seq.getOrdinate(0, CoordinateSequence.X) === seq.getOrdinate(n - 1, CoordinateSequence.X) && seq.getOrdinate(0, CoordinateSequence.Y) === seq.getOrdinate(n - 1, CoordinateSequence.Y)
98788         };
98789         CoordinateSequences.isEqual = function isEqual (cs1, cs2) {
98790           var cs1Size = cs1.size();
98791           var cs2Size = cs2.size();
98792           if (cs1Size !== cs2Size) { return false }
98793           var dim = Math.min(cs1.getDimension(), cs2.getDimension());
98794           for (var i = 0; i < cs1Size; i++) {
98795             for (var d = 0; d < dim; d++) {
98796               var v1 = cs1.getOrdinate(i, d);
98797               var v2 = cs2.getOrdinate(i, d);
98798               if (cs1.getOrdinate(i, d) === cs2.getOrdinate(i, d)) { continue }
98799               if (Double.isNaN(v1) && Double.isNaN(v2)) { continue }
98800               return false
98801             }
98802           }
98803           return true
98804         };
98805         CoordinateSequences.extend = function extend (fact, seq, size) {
98806           var newseq = fact.create(size, seq.getDimension());
98807           var n = seq.size();
98808           CoordinateSequences.copy(seq, 0, newseq, 0, n);
98809           if (n > 0) {
98810             for (var i = n; i < size; i++) { CoordinateSequences.copy(seq, n - 1, newseq, i, 1); }
98811           }
98812           return newseq
98813         };
98814         CoordinateSequences.reverse = function reverse (seq) {
98815           var last = seq.size() - 1;
98816           var mid = Math.trunc(last / 2);
98817           for (var i = 0; i <= mid; i++) {
98818             CoordinateSequences.swap(seq, i, last - i);
98819           }
98820         };
98821         CoordinateSequences.swap = function swap (seq, i, j) {
98822           if (i === j) { return null }
98823           for (var dim = 0; dim < seq.getDimension(); dim++) {
98824             var tmp = seq.getOrdinate(i, dim);
98825             seq.setOrdinate(i, dim, seq.getOrdinate(j, dim));
98826             seq.setOrdinate(j, dim, tmp);
98827           }
98828         };
98829         CoordinateSequences.copy = function copy (src, srcPos, dest, destPos, length) {
98830           for (var i = 0; i < length; i++) {
98831             CoordinateSequences.copyCoord(src, srcPos + i, dest, destPos + i);
98832           }
98833         };
98834         CoordinateSequences.toString = function toString () {
98835           if (arguments.length === 1) {
98836             var cs = arguments[0];
98837             var size = cs.size();
98838             if (size === 0) { return '()' }
98839             var dim = cs.getDimension();
98840             var buf = new StringBuffer();
98841             buf.append('(');
98842             for (var i = 0; i < size; i++) {
98843               if (i > 0) { buf.append(' '); }
98844               for (var d = 0; d < dim; d++) {
98845                 if (d > 0) { buf.append(','); }
98846                 buf.append(StringUtil.toString(cs.getOrdinate(i, d)));
98847               }
98848             }
98849             buf.append(')');
98850             return buf.toString()
98851           }
98852         };
98853         CoordinateSequences.ensureValidRing = function ensureValidRing (fact, seq) {
98854           var n = seq.size();
98855           if (n === 0) { return seq }
98856           if (n <= 3) { return CoordinateSequences.createClosedRing(fact, seq, 4) }
98857           var isClosed = seq.getOrdinate(0, CoordinateSequence.X) === seq.getOrdinate(n - 1, CoordinateSequence.X) && seq.getOrdinate(0, CoordinateSequence.Y) === seq.getOrdinate(n - 1, CoordinateSequence.Y);
98858           if (isClosed) { return seq }
98859           return CoordinateSequences.createClosedRing(fact, seq, n + 1)
98860         };
98861         CoordinateSequences.createClosedRing = function createClosedRing (fact, seq, size) {
98862           var newseq = fact.create(size, seq.getDimension());
98863           var n = seq.size();
98864           CoordinateSequences.copy(seq, 0, newseq, 0, n);
98865           for (var i = n; i < size; i++) { CoordinateSequences.copy(seq, 0, newseq, i, 1); }
98866           return newseq
98867         };
98868
98869         var LineString = (function (Geometry$$1) {
98870           function LineString (points, factory) {
98871             Geometry$$1.call(this, factory);
98872             this._points = null;
98873             this.init(points);
98874           }
98875
98876           if ( Geometry$$1 ) { LineString.__proto__ = Geometry$$1; }
98877           LineString.prototype = Object.create( Geometry$$1 && Geometry$$1.prototype );
98878           LineString.prototype.constructor = LineString;
98879
98880           var staticAccessors = { serialVersionUID: { configurable: true } };
98881           LineString.prototype.computeEnvelopeInternal = function computeEnvelopeInternal () {
98882             if (this.isEmpty()) {
98883               return new Envelope()
98884             }
98885             return this._points.expandEnvelope(new Envelope())
98886           };
98887           LineString.prototype.isRing = function isRing () {
98888             return this.isClosed() && this.isSimple()
98889           };
98890           LineString.prototype.getSortIndex = function getSortIndex () {
98891             return Geometry$$1.SORTINDEX_LINESTRING
98892           };
98893           LineString.prototype.getCoordinates = function getCoordinates () {
98894             return this._points.toCoordinateArray()
98895           };
98896           LineString.prototype.equalsExact = function equalsExact () {
98897             var this$1 = this;
98898
98899             if (arguments.length === 2) {
98900               var other = arguments[0];
98901               var tolerance = arguments[1];
98902               if (!this.isEquivalentClass(other)) {
98903                 return false
98904               }
98905               var otherLineString = other;
98906               if (this._points.size() !== otherLineString._points.size()) {
98907                 return false
98908               }
98909               for (var i = 0; i < this._points.size(); i++) {
98910                 if (!this$1.equal(this$1._points.getCoordinate(i), otherLineString._points.getCoordinate(i), tolerance)) {
98911                   return false
98912                 }
98913               }
98914               return true
98915             } else { return Geometry$$1.prototype.equalsExact.apply(this, arguments) }
98916           };
98917           LineString.prototype.normalize = function normalize () {
98918             var this$1 = this;
98919
98920             for (var i = 0; i < Math.trunc(this._points.size() / 2); i++) {
98921               var j = this$1._points.size() - 1 - i;
98922               if (!this$1._points.getCoordinate(i).equals(this$1._points.getCoordinate(j))) {
98923                 if (this$1._points.getCoordinate(i).compareTo(this$1._points.getCoordinate(j)) > 0) {
98924                   CoordinateSequences.reverse(this$1._points);
98925                 }
98926                 return null
98927               }
98928             }
98929           };
98930           LineString.prototype.getCoordinate = function getCoordinate () {
98931             if (this.isEmpty()) { return null }
98932             return this._points.getCoordinate(0)
98933           };
98934           LineString.prototype.getBoundaryDimension = function getBoundaryDimension () {
98935             if (this.isClosed()) {
98936               return Dimension.FALSE
98937             }
98938             return 0
98939           };
98940           LineString.prototype.isClosed = function isClosed () {
98941             if (this.isEmpty()) {
98942               return false
98943             }
98944             return this.getCoordinateN(0).equals2D(this.getCoordinateN(this.getNumPoints() - 1))
98945           };
98946           LineString.prototype.getEndPoint = function getEndPoint () {
98947             if (this.isEmpty()) {
98948               return null
98949             }
98950             return this.getPointN(this.getNumPoints() - 1)
98951           };
98952           LineString.prototype.getDimension = function getDimension () {
98953             return 1
98954           };
98955           LineString.prototype.getLength = function getLength () {
98956             return CGAlgorithms.computeLength(this._points)
98957           };
98958           LineString.prototype.getNumPoints = function getNumPoints () {
98959             return this._points.size()
98960           };
98961           LineString.prototype.reverse = function reverse () {
98962             var seq = this._points.copy();
98963             CoordinateSequences.reverse(seq);
98964             var revLine = this.getFactory().createLineString(seq);
98965             return revLine
98966           };
98967           LineString.prototype.compareToSameClass = function compareToSameClass () {
98968             var this$1 = this;
98969
98970             if (arguments.length === 1) {
98971               var o = arguments[0];
98972               var line = o;
98973               var i = 0;
98974               var j = 0;
98975               while (i < this._points.size() && j < line._points.size()) {
98976                 var comparison = this$1._points.getCoordinate(i).compareTo(line._points.getCoordinate(j));
98977                 if (comparison !== 0) {
98978                   return comparison
98979                 }
98980                 i++;
98981                 j++;
98982               }
98983               if (i < this._points.size()) {
98984                 return 1
98985               }
98986               if (j < line._points.size()) {
98987                 return -1
98988               }
98989               return 0
98990             } else if (arguments.length === 2) {
98991               var o$1 = arguments[0];
98992               var comp = arguments[1];
98993               var line$1 = o$1;
98994               return comp.compare(this._points, line$1._points)
98995             }
98996           };
98997           LineString.prototype.apply = function apply () {
98998             var this$1 = this;
98999
99000             if (hasInterface(arguments[0], CoordinateFilter)) {
99001               var filter = arguments[0];
99002               for (var i = 0; i < this._points.size(); i++) {
99003                 filter.filter(this$1._points.getCoordinate(i));
99004               }
99005             } else if (hasInterface(arguments[0], CoordinateSequenceFilter)) {
99006               var filter$1 = arguments[0];
99007               if (this._points.size() === 0) { return null }
99008               for (var i$1 = 0; i$1 < this._points.size(); i$1++) {
99009                 filter$1.filter(this$1._points, i$1);
99010                 if (filter$1.isDone()) { break }
99011               }
99012               if (filter$1.isGeometryChanged()) { this.geometryChanged(); }
99013             } else if (hasInterface(arguments[0], GeometryFilter)) {
99014               var filter$2 = arguments[0];
99015               filter$2.filter(this);
99016             } else if (hasInterface(arguments[0], GeometryComponentFilter)) {
99017               var filter$3 = arguments[0];
99018               filter$3.filter(this);
99019             }
99020           };
99021           LineString.prototype.getBoundary = function getBoundary () {
99022             return new BoundaryOp(this).getBoundary()
99023           };
99024           LineString.prototype.isEquivalentClass = function isEquivalentClass (other) {
99025             return other instanceof LineString
99026           };
99027           LineString.prototype.clone = function clone () {
99028             var ls = Geometry$$1.prototype.clone.call(this);
99029             ls._points = this._points.clone();
99030             return ls
99031           };
99032           LineString.prototype.getCoordinateN = function getCoordinateN (n) {
99033             return this._points.getCoordinate(n)
99034           };
99035           LineString.prototype.getGeometryType = function getGeometryType () {
99036             return 'LineString'
99037           };
99038           LineString.prototype.copy = function copy () {
99039             return new LineString(this._points.copy(), this._factory)
99040           };
99041           LineString.prototype.getCoordinateSequence = function getCoordinateSequence () {
99042             return this._points
99043           };
99044           LineString.prototype.isEmpty = function isEmpty () {
99045             return this._points.size() === 0
99046           };
99047           LineString.prototype.init = function init (points) {
99048             if (points === null) {
99049               points = this.getFactory().getCoordinateSequenceFactory().create([]);
99050             }
99051             if (points.size() === 1) {
99052               throw new IllegalArgumentException('Invalid number of points in LineString (found ' + points.size() + ' - must be 0 or >= 2)')
99053             }
99054             this._points = points;
99055           };
99056           LineString.prototype.isCoordinate = function isCoordinate (pt) {
99057             var this$1 = this;
99058
99059             for (var i = 0; i < this._points.size(); i++) {
99060               if (this$1._points.getCoordinate(i).equals(pt)) {
99061                 return true
99062               }
99063             }
99064             return false
99065           };
99066           LineString.prototype.getStartPoint = function getStartPoint () {
99067             if (this.isEmpty()) {
99068               return null
99069             }
99070             return this.getPointN(0)
99071           };
99072           LineString.prototype.getPointN = function getPointN (n) {
99073             return this.getFactory().createPoint(this._points.getCoordinate(n))
99074           };
99075           LineString.prototype.interfaces_ = function interfaces_ () {
99076             return [Lineal]
99077           };
99078           LineString.prototype.getClass = function getClass () {
99079             return LineString
99080           };
99081           staticAccessors.serialVersionUID.get = function () { return 3110669828065365560 };
99082
99083           Object.defineProperties( LineString, staticAccessors );
99084
99085           return LineString;
99086         }(Geometry));
99087
99088         var Puntal = function Puntal () {};
99089
99090         Puntal.prototype.interfaces_ = function interfaces_ () {
99091           return []
99092         };
99093         Puntal.prototype.getClass = function getClass () {
99094           return Puntal
99095         };
99096
99097         var Point$1 = (function (Geometry$$1) {
99098           function Point (coordinates, factory) {
99099             Geometry$$1.call(this, factory);
99100             this._coordinates = coordinates || null;
99101             this.init(this._coordinates);
99102           }
99103
99104           if ( Geometry$$1 ) { Point.__proto__ = Geometry$$1; }
99105           Point.prototype = Object.create( Geometry$$1 && Geometry$$1.prototype );
99106           Point.prototype.constructor = Point;
99107
99108           var staticAccessors = { serialVersionUID: { configurable: true } };
99109           Point.prototype.computeEnvelopeInternal = function computeEnvelopeInternal () {
99110             if (this.isEmpty()) {
99111               return new Envelope()
99112             }
99113             var env = new Envelope();
99114             env.expandToInclude(this._coordinates.getX(0), this._coordinates.getY(0));
99115             return env
99116           };
99117           Point.prototype.getSortIndex = function getSortIndex () {
99118             return Geometry$$1.SORTINDEX_POINT
99119           };
99120           Point.prototype.getCoordinates = function getCoordinates () {
99121             return this.isEmpty() ? [] : [this.getCoordinate()]
99122           };
99123           Point.prototype.equalsExact = function equalsExact () {
99124             if (arguments.length === 2) {
99125               var other = arguments[0];
99126               var tolerance = arguments[1];
99127               if (!this.isEquivalentClass(other)) {
99128                 return false
99129               }
99130               if (this.isEmpty() && other.isEmpty()) {
99131                 return true
99132               }
99133               if (this.isEmpty() !== other.isEmpty()) {
99134                 return false
99135               }
99136               return this.equal(other.getCoordinate(), this.getCoordinate(), tolerance)
99137             } else { return Geometry$$1.prototype.equalsExact.apply(this, arguments) }
99138           };
99139           Point.prototype.normalize = function normalize () {};
99140           Point.prototype.getCoordinate = function getCoordinate () {
99141             return this._coordinates.size() !== 0 ? this._coordinates.getCoordinate(0) : null
99142           };
99143           Point.prototype.getBoundaryDimension = function getBoundaryDimension () {
99144             return Dimension.FALSE
99145           };
99146           Point.prototype.getDimension = function getDimension () {
99147             return 0
99148           };
99149           Point.prototype.getNumPoints = function getNumPoints () {
99150             return this.isEmpty() ? 0 : 1
99151           };
99152           Point.prototype.reverse = function reverse () {
99153             return this.copy()
99154           };
99155           Point.prototype.getX = function getX () {
99156             if (this.getCoordinate() === null) {
99157               throw new Error('getX called on empty Point')
99158             }
99159             return this.getCoordinate().x
99160           };
99161           Point.prototype.compareToSameClass = function compareToSameClass () {
99162             if (arguments.length === 1) {
99163               var other = arguments[0];
99164               var point$1 = other;
99165               return this.getCoordinate().compareTo(point$1.getCoordinate())
99166             } else if (arguments.length === 2) {
99167               var other$1 = arguments[0];
99168               var comp = arguments[1];
99169               var point = other$1;
99170               return comp.compare(this._coordinates, point._coordinates)
99171             }
99172           };
99173           Point.prototype.apply = function apply () {
99174             if (hasInterface(arguments[0], CoordinateFilter)) {
99175               var filter = arguments[0];
99176               if (this.isEmpty()) {
99177                 return null
99178               }
99179               filter.filter(this.getCoordinate());
99180             } else if (hasInterface(arguments[0], CoordinateSequenceFilter)) {
99181               var filter$1 = arguments[0];
99182               if (this.isEmpty()) { return null }
99183               filter$1.filter(this._coordinates, 0);
99184               if (filter$1.isGeometryChanged()) { this.geometryChanged(); }
99185             } else if (hasInterface(arguments[0], GeometryFilter)) {
99186               var filter$2 = arguments[0];
99187               filter$2.filter(this);
99188             } else if (hasInterface(arguments[0], GeometryComponentFilter)) {
99189               var filter$3 = arguments[0];
99190               filter$3.filter(this);
99191             }
99192           };
99193           Point.prototype.getBoundary = function getBoundary () {
99194             return this.getFactory().createGeometryCollection(null)
99195           };
99196           Point.prototype.clone = function clone () {
99197             var p = Geometry$$1.prototype.clone.call(this);
99198             p._coordinates = this._coordinates.clone();
99199             return p
99200           };
99201           Point.prototype.getGeometryType = function getGeometryType () {
99202             return 'Point'
99203           };
99204           Point.prototype.copy = function copy () {
99205             return new Point(this._coordinates.copy(), this._factory)
99206           };
99207           Point.prototype.getCoordinateSequence = function getCoordinateSequence () {
99208             return this._coordinates
99209           };
99210           Point.prototype.getY = function getY () {
99211             if (this.getCoordinate() === null) {
99212               throw new Error('getY called on empty Point')
99213             }
99214             return this.getCoordinate().y
99215           };
99216           Point.prototype.isEmpty = function isEmpty () {
99217             return this._coordinates.size() === 0
99218           };
99219           Point.prototype.init = function init (coordinates) {
99220             if (coordinates === null) {
99221               coordinates = this.getFactory().getCoordinateSequenceFactory().create([]);
99222             }
99223             Assert.isTrue(coordinates.size() <= 1);
99224             this._coordinates = coordinates;
99225           };
99226           Point.prototype.isSimple = function isSimple () {
99227             return true
99228           };
99229           Point.prototype.interfaces_ = function interfaces_ () {
99230             return [Puntal]
99231           };
99232           Point.prototype.getClass = function getClass () {
99233             return Point
99234           };
99235           staticAccessors.serialVersionUID.get = function () { return 4902022702746614570 };
99236
99237           Object.defineProperties( Point, staticAccessors );
99238
99239           return Point;
99240         }(Geometry));
99241
99242         var Polygonal = function Polygonal () {};
99243
99244         Polygonal.prototype.interfaces_ = function interfaces_ () {
99245           return []
99246         };
99247         Polygonal.prototype.getClass = function getClass () {
99248           return Polygonal
99249         };
99250
99251         var Polygon = (function (Geometry$$1) {
99252           function Polygon (shell, holes, factory) {
99253             Geometry$$1.call(this, factory);
99254             this._shell = null;
99255             this._holes = null;
99256             if (shell === null) {
99257               shell = this.getFactory().createLinearRing();
99258             }
99259             if (holes === null) {
99260               holes = [];
99261             }
99262             if (Geometry$$1.hasNullElements(holes)) {
99263               throw new IllegalArgumentException('holes must not contain null elements')
99264             }
99265             if (shell.isEmpty() && Geometry$$1.hasNonEmptyElements(holes)) {
99266               throw new IllegalArgumentException('shell is empty but holes are not')
99267             }
99268             this._shell = shell;
99269             this._holes = holes;
99270           }
99271
99272           if ( Geometry$$1 ) { Polygon.__proto__ = Geometry$$1; }
99273           Polygon.prototype = Object.create( Geometry$$1 && Geometry$$1.prototype );
99274           Polygon.prototype.constructor = Polygon;
99275
99276           var staticAccessors = { serialVersionUID: { configurable: true } };
99277           Polygon.prototype.computeEnvelopeInternal = function computeEnvelopeInternal () {
99278             return this._shell.getEnvelopeInternal()
99279           };
99280           Polygon.prototype.getSortIndex = function getSortIndex () {
99281             return Geometry$$1.SORTINDEX_POLYGON
99282           };
99283           Polygon.prototype.getCoordinates = function getCoordinates () {
99284             var this$1 = this;
99285
99286             if (this.isEmpty()) {
99287               return []
99288             }
99289             var coordinates = new Array(this.getNumPoints()).fill(null);
99290             var k = -1;
99291             var shellCoordinates = this._shell.getCoordinates();
99292             for (var x = 0; x < shellCoordinates.length; x++) {
99293               k++;
99294               coordinates[k] = shellCoordinates[x];
99295             }
99296             for (var i = 0; i < this._holes.length; i++) {
99297               var childCoordinates = this$1._holes[i].getCoordinates();
99298               for (var j = 0; j < childCoordinates.length; j++) {
99299                 k++;
99300                 coordinates[k] = childCoordinates[j];
99301               }
99302             }
99303             return coordinates
99304           };
99305           Polygon.prototype.getArea = function getArea () {
99306             var this$1 = this;
99307
99308             var area = 0.0;
99309             area += Math.abs(CGAlgorithms.signedArea(this._shell.getCoordinateSequence()));
99310             for (var i = 0; i < this._holes.length; i++) {
99311               area -= Math.abs(CGAlgorithms.signedArea(this$1._holes[i].getCoordinateSequence()));
99312             }
99313             return area
99314           };
99315           Polygon.prototype.isRectangle = function isRectangle () {
99316             if (this.getNumInteriorRing() !== 0) { return false }
99317             if (this._shell === null) { return false }
99318             if (this._shell.getNumPoints() !== 5) { return false }
99319             var seq = this._shell.getCoordinateSequence();
99320             var env = this.getEnvelopeInternal();
99321             for (var i = 0; i < 5; i++) {
99322               var x = seq.getX(i);
99323               if (!(x === env.getMinX() || x === env.getMaxX())) { return false }
99324               var y = seq.getY(i);
99325               if (!(y === env.getMinY() || y === env.getMaxY())) { return false }
99326             }
99327             var prevX = seq.getX(0);
99328             var prevY = seq.getY(0);
99329             for (var i$1 = 1; i$1 <= 4; i$1++) {
99330               var x$1 = seq.getX(i$1);
99331               var y$1 = seq.getY(i$1);
99332               var xChanged = x$1 !== prevX;
99333               var yChanged = y$1 !== prevY;
99334               if (xChanged === yChanged) { return false }
99335               prevX = x$1;
99336               prevY = y$1;
99337             }
99338             return true
99339           };
99340           Polygon.prototype.equalsExact = function equalsExact () {
99341             var this$1 = this;
99342
99343             if (arguments.length === 2) {
99344               var other = arguments[0];
99345               var tolerance = arguments[1];
99346               if (!this.isEquivalentClass(other)) {
99347                 return false
99348               }
99349               var otherPolygon = other;
99350               var thisShell = this._shell;
99351               var otherPolygonShell = otherPolygon._shell;
99352               if (!thisShell.equalsExact(otherPolygonShell, tolerance)) {
99353                 return false
99354               }
99355               if (this._holes.length !== otherPolygon._holes.length) {
99356                 return false
99357               }
99358               for (var i = 0; i < this._holes.length; i++) {
99359                 if (!this$1._holes[i].equalsExact(otherPolygon._holes[i], tolerance)) {
99360                   return false
99361                 }
99362               }
99363               return true
99364             } else { return Geometry$$1.prototype.equalsExact.apply(this, arguments) }
99365           };
99366           Polygon.prototype.normalize = function normalize () {
99367             var this$1 = this;
99368
99369             if (arguments.length === 0) {
99370               this.normalize(this._shell, true);
99371               for (var i = 0; i < this._holes.length; i++) {
99372                 this$1.normalize(this$1._holes[i], false);
99373               }
99374               Arrays.sort(this._holes);
99375             } else if (arguments.length === 2) {
99376               var ring = arguments[0];
99377               var clockwise = arguments[1];
99378               if (ring.isEmpty()) {
99379                 return null
99380               }
99381               var uniqueCoordinates = new Array(ring.getCoordinates().length - 1).fill(null);
99382               System.arraycopy(ring.getCoordinates(), 0, uniqueCoordinates, 0, uniqueCoordinates.length);
99383               var minCoordinate = CoordinateArrays.minCoordinate(ring.getCoordinates());
99384               CoordinateArrays.scroll(uniqueCoordinates, minCoordinate);
99385               System.arraycopy(uniqueCoordinates, 0, ring.getCoordinates(), 0, uniqueCoordinates.length);
99386               ring.getCoordinates()[uniqueCoordinates.length] = uniqueCoordinates[0];
99387               if (CGAlgorithms.isCCW(ring.getCoordinates()) === clockwise) {
99388                 CoordinateArrays.reverse(ring.getCoordinates());
99389               }
99390             }
99391           };
99392           Polygon.prototype.getCoordinate = function getCoordinate () {
99393             return this._shell.getCoordinate()
99394           };
99395           Polygon.prototype.getNumInteriorRing = function getNumInteriorRing () {
99396             return this._holes.length
99397           };
99398           Polygon.prototype.getBoundaryDimension = function getBoundaryDimension () {
99399             return 1
99400           };
99401           Polygon.prototype.getDimension = function getDimension () {
99402             return 2
99403           };
99404           Polygon.prototype.getLength = function getLength () {
99405             var this$1 = this;
99406
99407             var len = 0.0;
99408             len += this._shell.getLength();
99409             for (var i = 0; i < this._holes.length; i++) {
99410               len += this$1._holes[i].getLength();
99411             }
99412             return len
99413           };
99414           Polygon.prototype.getNumPoints = function getNumPoints () {
99415             var this$1 = this;
99416
99417             var numPoints = this._shell.getNumPoints();
99418             for (var i = 0; i < this._holes.length; i++) {
99419               numPoints += this$1._holes[i].getNumPoints();
99420             }
99421             return numPoints
99422           };
99423           Polygon.prototype.reverse = function reverse () {
99424             var this$1 = this;
99425
99426             var poly = this.copy();
99427             poly._shell = this._shell.copy().reverse();
99428             poly._holes = new Array(this._holes.length).fill(null);
99429             for (var i = 0; i < this._holes.length; i++) {
99430               poly._holes[i] = this$1._holes[i].copy().reverse();
99431             }
99432             return poly
99433           };
99434           Polygon.prototype.convexHull = function convexHull () {
99435             return this.getExteriorRing().convexHull()
99436           };
99437           Polygon.prototype.compareToSameClass = function compareToSameClass () {
99438             var this$1 = this;
99439
99440             if (arguments.length === 1) {
99441               var o = arguments[0];
99442               var thisShell = this._shell;
99443               var otherShell = o._shell;
99444               return thisShell.compareToSameClass(otherShell)
99445             } else if (arguments.length === 2) {
99446               var o$1 = arguments[0];
99447               var comp = arguments[1];
99448               var poly = o$1;
99449               var thisShell$1 = this._shell;
99450               var otherShell$1 = poly._shell;
99451               var shellComp = thisShell$1.compareToSameClass(otherShell$1, comp);
99452               if (shellComp !== 0) { return shellComp }
99453               var nHole1 = this.getNumInteriorRing();
99454               var nHole2 = poly.getNumInteriorRing();
99455               var i = 0;
99456               while (i < nHole1 && i < nHole2) {
99457                 var thisHole = this$1.getInteriorRingN(i);
99458                 var otherHole = poly.getInteriorRingN(i);
99459                 var holeComp = thisHole.compareToSameClass(otherHole, comp);
99460                 if (holeComp !== 0) { return holeComp }
99461                 i++;
99462               }
99463               if (i < nHole1) { return 1 }
99464               if (i < nHole2) { return -1 }
99465               return 0
99466             }
99467           };
99468           Polygon.prototype.apply = function apply (filter) {
99469             var this$1 = this;
99470
99471             if (hasInterface(filter, CoordinateFilter)) {
99472               this._shell.apply(filter);
99473               for (var i$1 = 0; i$1 < this._holes.length; i$1++) {
99474                 this$1._holes[i$1].apply(filter);
99475               }
99476             } else if (hasInterface(filter, CoordinateSequenceFilter)) {
99477               this._shell.apply(filter);
99478               if (!filter.isDone()) {
99479                 for (var i$2 = 0; i$2 < this._holes.length; i$2++) {
99480                   this$1._holes[i$2].apply(filter);
99481                   if (filter.isDone()) { break }
99482                 }
99483               }
99484               if (filter.isGeometryChanged()) { this.geometryChanged(); }
99485             } else if (hasInterface(filter, GeometryFilter)) {
99486               filter.filter(this);
99487             } else if (hasInterface(filter, GeometryComponentFilter)) {
99488               filter.filter(this);
99489               this._shell.apply(filter);
99490               for (var i = 0; i < this._holes.length; i++) {
99491                 this$1._holes[i].apply(filter);
99492               }
99493             }
99494           };
99495           Polygon.prototype.getBoundary = function getBoundary () {
99496             var this$1 = this;
99497
99498             if (this.isEmpty()) {
99499               return this.getFactory().createMultiLineString()
99500             }
99501             var rings = new Array(this._holes.length + 1).fill(null);
99502             rings[0] = this._shell;
99503             for (var i = 0; i < this._holes.length; i++) {
99504               rings[i + 1] = this$1._holes[i];
99505             }
99506             if (rings.length <= 1) { return this.getFactory().createLinearRing(rings[0].getCoordinateSequence()) }
99507             return this.getFactory().createMultiLineString(rings)
99508           };
99509           Polygon.prototype.clone = function clone () {
99510             var this$1 = this;
99511
99512             var poly = Geometry$$1.prototype.clone.call(this);
99513             poly._shell = this._shell.clone();
99514             poly._holes = new Array(this._holes.length).fill(null);
99515             for (var i = 0; i < this._holes.length; i++) {
99516               poly._holes[i] = this$1._holes[i].clone();
99517             }
99518             return poly
99519           };
99520           Polygon.prototype.getGeometryType = function getGeometryType () {
99521             return 'Polygon'
99522           };
99523           Polygon.prototype.copy = function copy () {
99524             var this$1 = this;
99525
99526             var shell = this._shell.copy();
99527             var holes = new Array(this._holes.length).fill(null);
99528             for (var i = 0; i < holes.length; i++) {
99529               holes[i] = this$1._holes[i].copy();
99530             }
99531             return new Polygon(shell, holes, this._factory)
99532           };
99533           Polygon.prototype.getExteriorRing = function getExteriorRing () {
99534             return this._shell
99535           };
99536           Polygon.prototype.isEmpty = function isEmpty () {
99537             return this._shell.isEmpty()
99538           };
99539           Polygon.prototype.getInteriorRingN = function getInteriorRingN (n) {
99540             return this._holes[n]
99541           };
99542           Polygon.prototype.interfaces_ = function interfaces_ () {
99543             return [Polygonal]
99544           };
99545           Polygon.prototype.getClass = function getClass () {
99546             return Polygon
99547           };
99548           staticAccessors.serialVersionUID.get = function () { return -3494792200821764533 };
99549
99550           Object.defineProperties( Polygon, staticAccessors );
99551
99552           return Polygon;
99553         }(Geometry));
99554
99555         var MultiPoint = (function (GeometryCollection$$1) {
99556           function MultiPoint () {
99557             GeometryCollection$$1.apply(this, arguments);
99558           }
99559
99560           if ( GeometryCollection$$1 ) { MultiPoint.__proto__ = GeometryCollection$$1; }
99561           MultiPoint.prototype = Object.create( GeometryCollection$$1 && GeometryCollection$$1.prototype );
99562           MultiPoint.prototype.constructor = MultiPoint;
99563
99564           var staticAccessors = { serialVersionUID: { configurable: true } };
99565
99566           MultiPoint.prototype.getSortIndex = function getSortIndex () {
99567             return Geometry.SORTINDEX_MULTIPOINT
99568           };
99569           MultiPoint.prototype.isValid = function isValid () {
99570             return true
99571           };
99572           MultiPoint.prototype.equalsExact = function equalsExact () {
99573             if (arguments.length === 2) {
99574               var other = arguments[0];
99575               var tolerance = arguments[1];
99576               if (!this.isEquivalentClass(other)) {
99577                 return false
99578               }
99579               return GeometryCollection$$1.prototype.equalsExact.call(this, other, tolerance)
99580             } else { return GeometryCollection$$1.prototype.equalsExact.apply(this, arguments) }
99581           };
99582           MultiPoint.prototype.getCoordinate = function getCoordinate () {
99583             if (arguments.length === 1) {
99584               var n = arguments[0];
99585               return this._geometries[n].getCoordinate()
99586             } else { return GeometryCollection$$1.prototype.getCoordinate.apply(this, arguments) }
99587           };
99588           MultiPoint.prototype.getBoundaryDimension = function getBoundaryDimension () {
99589             return Dimension.FALSE
99590           };
99591           MultiPoint.prototype.getDimension = function getDimension () {
99592             return 0
99593           };
99594           MultiPoint.prototype.getBoundary = function getBoundary () {
99595             return this.getFactory().createGeometryCollection(null)
99596           };
99597           MultiPoint.prototype.getGeometryType = function getGeometryType () {
99598             return 'MultiPoint'
99599           };
99600           MultiPoint.prototype.copy = function copy () {
99601             var this$1 = this;
99602
99603             var points = new Array(this._geometries.length).fill(null);
99604             for (var i = 0; i < points.length; i++) {
99605               points[i] = this$1._geometries[i].copy();
99606             }
99607             return new MultiPoint(points, this._factory)
99608           };
99609           MultiPoint.prototype.interfaces_ = function interfaces_ () {
99610             return [Puntal]
99611           };
99612           MultiPoint.prototype.getClass = function getClass () {
99613             return MultiPoint
99614           };
99615           staticAccessors.serialVersionUID.get = function () { return -8048474874175355449 };
99616
99617           Object.defineProperties( MultiPoint, staticAccessors );
99618
99619           return MultiPoint;
99620         }(GeometryCollection));
99621
99622         var LinearRing = (function (LineString$$1) {
99623           function LinearRing (points, factory) {
99624             if (points instanceof Coordinate && factory instanceof GeometryFactory) {
99625               points = factory.getCoordinateSequenceFactory().create(points);
99626             }
99627             LineString$$1.call(this, points, factory);
99628             this.validateConstruction();
99629           }
99630
99631           if ( LineString$$1 ) { LinearRing.__proto__ = LineString$$1; }
99632           LinearRing.prototype = Object.create( LineString$$1 && LineString$$1.prototype );
99633           LinearRing.prototype.constructor = LinearRing;
99634
99635           var staticAccessors = { MINIMUM_VALID_SIZE: { configurable: true },serialVersionUID: { configurable: true } };
99636           LinearRing.prototype.getSortIndex = function getSortIndex () {
99637             return Geometry.SORTINDEX_LINEARRING
99638           };
99639           LinearRing.prototype.getBoundaryDimension = function getBoundaryDimension () {
99640             return Dimension.FALSE
99641           };
99642           LinearRing.prototype.isClosed = function isClosed () {
99643             if (this.isEmpty()) {
99644               return true
99645             }
99646             return LineString$$1.prototype.isClosed.call(this)
99647           };
99648           LinearRing.prototype.reverse = function reverse () {
99649             var seq = this._points.copy();
99650             CoordinateSequences.reverse(seq);
99651             var rev = this.getFactory().createLinearRing(seq);
99652             return rev
99653           };
99654           LinearRing.prototype.validateConstruction = function validateConstruction () {
99655             if (!this.isEmpty() && !LineString$$1.prototype.isClosed.call(this)) {
99656               throw new IllegalArgumentException('Points of LinearRing do not form a closed linestring')
99657             }
99658             if (this.getCoordinateSequence().size() >= 1 && this.getCoordinateSequence().size() < LinearRing.MINIMUM_VALID_SIZE) {
99659               throw new IllegalArgumentException('Invalid number of points in LinearRing (found ' + this.getCoordinateSequence().size() + ' - must be 0 or >= 4)')
99660             }
99661           };
99662           LinearRing.prototype.getGeometryType = function getGeometryType () {
99663             return 'LinearRing'
99664           };
99665           LinearRing.prototype.copy = function copy () {
99666             return new LinearRing(this._points.copy(), this._factory)
99667           };
99668           LinearRing.prototype.interfaces_ = function interfaces_ () {
99669             return []
99670           };
99671           LinearRing.prototype.getClass = function getClass () {
99672             return LinearRing
99673           };
99674           staticAccessors.MINIMUM_VALID_SIZE.get = function () { return 4 };
99675           staticAccessors.serialVersionUID.get = function () { return -4261142084085851829 };
99676
99677           Object.defineProperties( LinearRing, staticAccessors );
99678
99679           return LinearRing;
99680         }(LineString));
99681
99682         var MultiPolygon = (function (GeometryCollection$$1) {
99683           function MultiPolygon () {
99684             GeometryCollection$$1.apply(this, arguments);
99685           }
99686
99687           if ( GeometryCollection$$1 ) { MultiPolygon.__proto__ = GeometryCollection$$1; }
99688           MultiPolygon.prototype = Object.create( GeometryCollection$$1 && GeometryCollection$$1.prototype );
99689           MultiPolygon.prototype.constructor = MultiPolygon;
99690
99691           var staticAccessors = { serialVersionUID: { configurable: true } };
99692
99693           MultiPolygon.prototype.getSortIndex = function getSortIndex () {
99694             return Geometry.SORTINDEX_MULTIPOLYGON
99695           };
99696           MultiPolygon.prototype.equalsExact = function equalsExact () {
99697             if (arguments.length === 2) {
99698               var other = arguments[0];
99699               var tolerance = arguments[1];
99700               if (!this.isEquivalentClass(other)) {
99701                 return false
99702               }
99703               return GeometryCollection$$1.prototype.equalsExact.call(this, other, tolerance)
99704             } else { return GeometryCollection$$1.prototype.equalsExact.apply(this, arguments) }
99705           };
99706           MultiPolygon.prototype.getBoundaryDimension = function getBoundaryDimension () {
99707             return 1
99708           };
99709           MultiPolygon.prototype.getDimension = function getDimension () {
99710             return 2
99711           };
99712           MultiPolygon.prototype.reverse = function reverse () {
99713             var this$1 = this;
99714
99715             var n = this._geometries.length;
99716             var revGeoms = new Array(n).fill(null);
99717             for (var i = 0; i < this._geometries.length; i++) {
99718               revGeoms[i] = this$1._geometries[i].reverse();
99719             }
99720             return this.getFactory().createMultiPolygon(revGeoms)
99721           };
99722           MultiPolygon.prototype.getBoundary = function getBoundary () {
99723             var this$1 = this;
99724
99725             if (this.isEmpty()) {
99726               return this.getFactory().createMultiLineString()
99727             }
99728             var allRings = new ArrayList();
99729             for (var i = 0; i < this._geometries.length; i++) {
99730               var polygon = this$1._geometries[i];
99731               var rings = polygon.getBoundary();
99732               for (var j = 0; j < rings.getNumGeometries(); j++) {
99733                 allRings.add(rings.getGeometryN(j));
99734               }
99735             }
99736             var allRingsArray = new Array(allRings.size()).fill(null);
99737             return this.getFactory().createMultiLineString(allRings.toArray(allRingsArray))
99738           };
99739           MultiPolygon.prototype.getGeometryType = function getGeometryType () {
99740             return 'MultiPolygon'
99741           };
99742           MultiPolygon.prototype.copy = function copy () {
99743             var this$1 = this;
99744
99745             var polygons = new Array(this._geometries.length).fill(null);
99746             for (var i = 0; i < polygons.length; i++) {
99747               polygons[i] = this$1._geometries[i].copy();
99748             }
99749             return new MultiPolygon(polygons, this._factory)
99750           };
99751           MultiPolygon.prototype.interfaces_ = function interfaces_ () {
99752             return [Polygonal]
99753           };
99754           MultiPolygon.prototype.getClass = function getClass () {
99755             return MultiPolygon
99756           };
99757           staticAccessors.serialVersionUID.get = function () { return -551033529766975875 };
99758
99759           Object.defineProperties( MultiPolygon, staticAccessors );
99760
99761           return MultiPolygon;
99762         }(GeometryCollection));
99763
99764         var GeometryEditor = function GeometryEditor (factory) {
99765           this._factory = factory || null;
99766           this._isUserDataCopied = false;
99767         };
99768
99769         var staticAccessors$16 = { NoOpGeometryOperation: { configurable: true },CoordinateOperation: { configurable: true },CoordinateSequenceOperation: { configurable: true } };
99770         GeometryEditor.prototype.setCopyUserData = function setCopyUserData (isUserDataCopied) {
99771           this._isUserDataCopied = isUserDataCopied;
99772         };
99773         GeometryEditor.prototype.edit = function edit (geometry, operation) {
99774           if (geometry === null) { return null }
99775           var result = this.editInternal(geometry, operation);
99776           if (this._isUserDataCopied) {
99777             result.setUserData(geometry.getUserData());
99778           }
99779           return result
99780         };
99781         GeometryEditor.prototype.editInternal = function editInternal (geometry, operation) {
99782           if (this._factory === null) { this._factory = geometry.getFactory(); }
99783           if (geometry instanceof GeometryCollection) {
99784             return this.editGeometryCollection(geometry, operation)
99785           }
99786           if (geometry instanceof Polygon) {
99787             return this.editPolygon(geometry, operation)
99788           }
99789           if (geometry instanceof Point$1) {
99790             return operation.edit(geometry, this._factory)
99791           }
99792           if (geometry instanceof LineString) {
99793             return operation.edit(geometry, this._factory)
99794           }
99795           Assert.shouldNeverReachHere('Unsupported Geometry class: ' + geometry.getClass().getName());
99796           return null
99797         };
99798         GeometryEditor.prototype.editGeometryCollection = function editGeometryCollection (collection, operation) {
99799             var this$1 = this;
99800
99801           var collectionForType = operation.edit(collection, this._factory);
99802           var geometries = new ArrayList();
99803           for (var i = 0; i < collectionForType.getNumGeometries(); i++) {
99804             var geometry = this$1.edit(collectionForType.getGeometryN(i), operation);
99805             if (geometry === null || geometry.isEmpty()) {
99806               continue
99807             }
99808             geometries.add(geometry);
99809           }
99810           if (collectionForType.getClass() === MultiPoint) {
99811             return this._factory.createMultiPoint(geometries.toArray([]))
99812           }
99813           if (collectionForType.getClass() === MultiLineString) {
99814             return this._factory.createMultiLineString(geometries.toArray([]))
99815           }
99816           if (collectionForType.getClass() === MultiPolygon) {
99817             return this._factory.createMultiPolygon(geometries.toArray([]))
99818           }
99819           return this._factory.createGeometryCollection(geometries.toArray([]))
99820         };
99821         GeometryEditor.prototype.editPolygon = function editPolygon (polygon, operation) {
99822             var this$1 = this;
99823
99824           var newPolygon = operation.edit(polygon, this._factory);
99825           if (newPolygon === null) { newPolygon = this._factory.createPolygon(null); }
99826           if (newPolygon.isEmpty()) {
99827             return newPolygon
99828           }
99829           var shell = this.edit(newPolygon.getExteriorRing(), operation);
99830           if (shell === null || shell.isEmpty()) {
99831             return this._factory.createPolygon()
99832           }
99833           var holes = new ArrayList();
99834           for (var i = 0; i < newPolygon.getNumInteriorRing(); i++) {
99835             var hole = this$1.edit(newPolygon.getInteriorRingN(i), operation);
99836             if (hole === null || hole.isEmpty()) {
99837               continue
99838             }
99839             holes.add(hole);
99840           }
99841           return this._factory.createPolygon(shell, holes.toArray([]))
99842         };
99843         GeometryEditor.prototype.interfaces_ = function interfaces_ () {
99844           return []
99845         };
99846         GeometryEditor.prototype.getClass = function getClass () {
99847           return GeometryEditor
99848         };
99849         GeometryEditor.GeometryEditorOperation = function GeometryEditorOperation () {};
99850         staticAccessors$16.NoOpGeometryOperation.get = function () { return NoOpGeometryOperation };
99851         staticAccessors$16.CoordinateOperation.get = function () { return CoordinateOperation };
99852         staticAccessors$16.CoordinateSequenceOperation.get = function () { return CoordinateSequenceOperation };
99853
99854         Object.defineProperties( GeometryEditor, staticAccessors$16 );
99855
99856         var NoOpGeometryOperation = function NoOpGeometryOperation () {};
99857
99858         NoOpGeometryOperation.prototype.edit = function edit (geometry, factory) {
99859           return geometry
99860         };
99861         NoOpGeometryOperation.prototype.interfaces_ = function interfaces_ () {
99862           return [GeometryEditor.GeometryEditorOperation]
99863         };
99864         NoOpGeometryOperation.prototype.getClass = function getClass () {
99865           return NoOpGeometryOperation
99866         };
99867
99868         var CoordinateOperation = function CoordinateOperation () {};
99869
99870         CoordinateOperation.prototype.edit = function edit (geometry, factory) {
99871           var coords = this.editCoordinates(geometry.getCoordinates(), geometry);
99872           if (coords === null) { return geometry }
99873           if (geometry instanceof LinearRing) {
99874             return factory.createLinearRing(coords)
99875           }
99876           if (geometry instanceof LineString) {
99877             return factory.createLineString(coords)
99878           }
99879           if (geometry instanceof Point$1) {
99880             if (coords.length > 0) {
99881               return factory.createPoint(coords[0])
99882             } else {
99883               return factory.createPoint()
99884             }
99885           }
99886           return geometry
99887         };
99888         CoordinateOperation.prototype.interfaces_ = function interfaces_ () {
99889           return [GeometryEditor.GeometryEditorOperation]
99890         };
99891         CoordinateOperation.prototype.getClass = function getClass () {
99892           return CoordinateOperation
99893         };
99894
99895         var CoordinateSequenceOperation = function CoordinateSequenceOperation () {};
99896
99897         CoordinateSequenceOperation.prototype.edit = function edit (geometry, factory) {
99898           if (geometry instanceof LinearRing) {
99899             return factory.createLinearRing(this.edit(geometry.getCoordinateSequence(), geometry))
99900           }
99901           if (geometry instanceof LineString) {
99902             return factory.createLineString(this.edit(geometry.getCoordinateSequence(), geometry))
99903           }
99904           if (geometry instanceof Point$1) {
99905             return factory.createPoint(this.edit(geometry.getCoordinateSequence(), geometry))
99906           }
99907           return geometry
99908         };
99909         CoordinateSequenceOperation.prototype.interfaces_ = function interfaces_ () {
99910           return [GeometryEditor.GeometryEditorOperation]
99911         };
99912         CoordinateSequenceOperation.prototype.getClass = function getClass () {
99913           return CoordinateSequenceOperation
99914         };
99915
99916         var CoordinateArraySequence = function CoordinateArraySequence () {
99917           var this$1 = this;
99918
99919           this._dimension = 3;
99920           this._coordinates = null;
99921           if (arguments.length === 1) {
99922             if (arguments[0] instanceof Array) {
99923               this._coordinates = arguments[0];
99924               this._dimension = 3;
99925             } else if (Number.isInteger(arguments[0])) {
99926               var size = arguments[0];
99927               this._coordinates = new Array(size).fill(null);
99928               for (var i = 0; i < size; i++) {
99929                 this$1._coordinates[i] = new Coordinate();
99930               }
99931             } else if (hasInterface(arguments[0], CoordinateSequence)) {
99932               var coordSeq = arguments[0];
99933               if (coordSeq === null) {
99934                 this._coordinates = new Array(0).fill(null);
99935                 return null
99936               }
99937               this._dimension = coordSeq.getDimension();
99938               this._coordinates = new Array(coordSeq.size()).fill(null);
99939               for (var i$1 = 0; i$1 < this._coordinates.length; i$1++) {
99940                 this$1._coordinates[i$1] = coordSeq.getCoordinateCopy(i$1);
99941               }
99942             }
99943           } else if (arguments.length === 2) {
99944             if (arguments[0] instanceof Array && Number.isInteger(arguments[1])) {
99945               var coordinates = arguments[0];
99946               var dimension = arguments[1];
99947               this._coordinates = coordinates;
99948               this._dimension = dimension;
99949               if (coordinates === null) { this._coordinates = new Array(0).fill(null); }
99950             } else if (Number.isInteger(arguments[0]) && Number.isInteger(arguments[1])) {
99951               var size$1 = arguments[0];
99952               var dimension$1 = arguments[1];
99953               this._coordinates = new Array(size$1).fill(null);
99954               this._dimension = dimension$1;
99955               for (var i$2 = 0; i$2 < size$1; i$2++) {
99956                 this$1._coordinates[i$2] = new Coordinate();
99957               }
99958             }
99959           }
99960         };
99961
99962         var staticAccessors$18 = { serialVersionUID: { configurable: true } };
99963         CoordinateArraySequence.prototype.setOrdinate = function setOrdinate (index, ordinateIndex, value) {
99964           switch (ordinateIndex) {
99965             case CoordinateSequence.X:
99966               this._coordinates[index].x = value;
99967               break
99968             case CoordinateSequence.Y:
99969               this._coordinates[index].y = value;
99970               break
99971             case CoordinateSequence.Z:
99972               this._coordinates[index].z = value;
99973               break
99974             default:
99975               throw new IllegalArgumentException('invalid ordinateIndex')
99976           }
99977         };
99978         CoordinateArraySequence.prototype.size = function size () {
99979           return this._coordinates.length
99980         };
99981         CoordinateArraySequence.prototype.getOrdinate = function getOrdinate (index, ordinateIndex) {
99982           switch (ordinateIndex) {
99983             case CoordinateSequence.X:
99984               return this._coordinates[index].x
99985             case CoordinateSequence.Y:
99986               return this._coordinates[index].y
99987             case CoordinateSequence.Z:
99988               return this._coordinates[index].z
99989           }
99990           return Double.NaN
99991         };
99992         CoordinateArraySequence.prototype.getCoordinate = function getCoordinate () {
99993           if (arguments.length === 1) {
99994             var i = arguments[0];
99995             return this._coordinates[i]
99996           } else if (arguments.length === 2) {
99997             var index = arguments[0];
99998             var coord = arguments[1];
99999             coord.x = this._coordinates[index].x;
100000             coord.y = this._coordinates[index].y;
100001             coord.z = this._coordinates[index].z;
100002           }
100003         };
100004         CoordinateArraySequence.prototype.getCoordinateCopy = function getCoordinateCopy (i) {
100005           return new Coordinate(this._coordinates[i])
100006         };
100007         CoordinateArraySequence.prototype.getDimension = function getDimension () {
100008           return this._dimension
100009         };
100010         CoordinateArraySequence.prototype.getX = function getX (index) {
100011           return this._coordinates[index].x
100012         };
100013         CoordinateArraySequence.prototype.clone = function clone () {
100014             var this$1 = this;
100015
100016           var cloneCoordinates = new Array(this.size()).fill(null);
100017           for (var i = 0; i < this._coordinates.length; i++) {
100018             cloneCoordinates[i] = this$1._coordinates[i].clone();
100019           }
100020           return new CoordinateArraySequence(cloneCoordinates, this._dimension)
100021         };
100022         CoordinateArraySequence.prototype.expandEnvelope = function expandEnvelope (env) {
100023             var this$1 = this;
100024
100025           for (var i = 0; i < this._coordinates.length; i++) {
100026             env.expandToInclude(this$1._coordinates[i]);
100027           }
100028           return env
100029         };
100030         CoordinateArraySequence.prototype.copy = function copy () {
100031             var this$1 = this;
100032
100033           var cloneCoordinates = new Array(this.size()).fill(null);
100034           for (var i = 0; i < this._coordinates.length; i++) {
100035             cloneCoordinates[i] = this$1._coordinates[i].copy();
100036           }
100037           return new CoordinateArraySequence(cloneCoordinates, this._dimension)
100038         };
100039         CoordinateArraySequence.prototype.toString = function toString () {
100040             var this$1 = this;
100041
100042           if (this._coordinates.length > 0) {
100043             var strBuf = new StringBuffer(17 * this._coordinates.length);
100044             strBuf.append('(');
100045             strBuf.append(this._coordinates[0]);
100046             for (var i = 1; i < this._coordinates.length; i++) {
100047               strBuf.append(', ');
100048               strBuf.append(this$1._coordinates[i]);
100049             }
100050             strBuf.append(')');
100051             return strBuf.toString()
100052           } else {
100053             return '()'
100054           }
100055         };
100056         CoordinateArraySequence.prototype.getY = function getY (index) {
100057           return this._coordinates[index].y
100058         };
100059         CoordinateArraySequence.prototype.toCoordinateArray = function toCoordinateArray () {
100060           return this._coordinates
100061         };
100062         CoordinateArraySequence.prototype.interfaces_ = function interfaces_ () {
100063           return [CoordinateSequence, Serializable]
100064         };
100065         CoordinateArraySequence.prototype.getClass = function getClass () {
100066           return CoordinateArraySequence
100067         };
100068         staticAccessors$18.serialVersionUID.get = function () { return -915438501601840650 };
100069
100070         Object.defineProperties( CoordinateArraySequence, staticAccessors$18 );
100071
100072         var CoordinateArraySequenceFactory = function CoordinateArraySequenceFactory () {};
100073
100074         var staticAccessors$17 = { serialVersionUID: { configurable: true },instanceObject: { configurable: true } };
100075
100076         CoordinateArraySequenceFactory.prototype.readResolve = function readResolve () {
100077           return CoordinateArraySequenceFactory.instance()
100078         };
100079         CoordinateArraySequenceFactory.prototype.create = function create () {
100080           if (arguments.length === 1) {
100081             if (arguments[0] instanceof Array) {
100082               var coordinates = arguments[0];
100083               return new CoordinateArraySequence(coordinates)
100084             } else if (hasInterface(arguments[0], CoordinateSequence)) {
100085               var coordSeq = arguments[0];
100086               return new CoordinateArraySequence(coordSeq)
100087             }
100088           } else if (arguments.length === 2) {
100089             var size = arguments[0];
100090             var dimension = arguments[1];
100091             if (dimension > 3) { dimension = 3; }
100092             if (dimension < 2) { return new CoordinateArraySequence(size) }
100093             return new CoordinateArraySequence(size, dimension)
100094           }
100095         };
100096         CoordinateArraySequenceFactory.prototype.interfaces_ = function interfaces_ () {
100097           return [CoordinateSequenceFactory, Serializable]
100098         };
100099         CoordinateArraySequenceFactory.prototype.getClass = function getClass () {
100100           return CoordinateArraySequenceFactory
100101         };
100102         CoordinateArraySequenceFactory.instance = function instance () {
100103           return CoordinateArraySequenceFactory.instanceObject
100104         };
100105
100106         staticAccessors$17.serialVersionUID.get = function () { return -4099577099607551657 };
100107         staticAccessors$17.instanceObject.get = function () { return new CoordinateArraySequenceFactory() };
100108
100109         Object.defineProperties( CoordinateArraySequenceFactory, staticAccessors$17 );
100110
100111         /**
100112          * @see http://download.oracle.com/javase/6/docs/api/java/util/HashMap.html
100113          *
100114          * @extends {javascript.util.Map}
100115          * @constructor
100116          * @private
100117          */
100118         var HashMap = (function (MapInterface) {
100119           function HashMap () {
100120             MapInterface.call(this);
100121             this.map_ = new Map();
100122           }
100123
100124           if ( MapInterface ) { HashMap.__proto__ = MapInterface; }
100125           HashMap.prototype = Object.create( MapInterface && MapInterface.prototype );
100126           HashMap.prototype.constructor = HashMap;
100127           /**
100128            * @override
100129            */
100130           HashMap.prototype.get = function get (key) {
100131             return this.map_.get(key) || null
100132           };
100133
100134           /**
100135            * @override
100136            */
100137           HashMap.prototype.put = function put (key, value) {
100138             this.map_.set(key, value);
100139             return value
100140           };
100141
100142           /**
100143            * @override
100144            */
100145           HashMap.prototype.values = function values () {
100146             var arrayList = new ArrayList();
100147             var it = this.map_.values();
100148             var o = it.next();
100149             while (!o.done) {
100150               arrayList.add(o.value);
100151               o = it.next();
100152             }
100153             return arrayList
100154           };
100155
100156           /**
100157            * @override
100158            */
100159           HashMap.prototype.entrySet = function entrySet () {
100160             var hashSet = new HashSet();
100161             this.map_.entries().forEach(function (entry) { return hashSet.add(entry); });
100162             return hashSet
100163           };
100164
100165           /**
100166            * @override
100167            */
100168           HashMap.prototype.size = function size () {
100169             return this.map_.size()
100170           };
100171
100172           return HashMap;
100173         }(Map$1$1));
100174
100175         var PrecisionModel = function PrecisionModel () {
100176           this._modelType = null;
100177           this._scale = null;
100178           if (arguments.length === 0) {
100179             this._modelType = PrecisionModel.FLOATING;
100180           } else if (arguments.length === 1) {
100181             if (arguments[0] instanceof Type$2) {
100182               var modelType = arguments[0];
100183               this._modelType = modelType;
100184               if (modelType === PrecisionModel.FIXED) {
100185                 this.setScale(1.0);
100186               }
100187             } else if (typeof arguments[0] === 'number') {
100188               var scale = arguments[0];
100189               this._modelType = PrecisionModel.FIXED;
100190               this.setScale(scale);
100191             } else if (arguments[0] instanceof PrecisionModel) {
100192               var pm = arguments[0];
100193               this._modelType = pm._modelType;
100194               this._scale = pm._scale;
100195             }
100196           }
100197         };
100198
100199         var staticAccessors$19 = { serialVersionUID: { configurable: true },maximumPreciseValue: { configurable: true } };
100200         PrecisionModel.prototype.equals = function equals (other) {
100201           if (!(other instanceof PrecisionModel)) {
100202             return false
100203           }
100204           var otherPrecisionModel = other;
100205           return this._modelType === otherPrecisionModel._modelType && this._scale === otherPrecisionModel._scale
100206         };
100207         PrecisionModel.prototype.compareTo = function compareTo (o) {
100208           var other = o;
100209           var sigDigits = this.getMaximumSignificantDigits();
100210           var otherSigDigits = other.getMaximumSignificantDigits();
100211           return new Integer(sigDigits).compareTo(new Integer(otherSigDigits))
100212         };
100213         PrecisionModel.prototype.getScale = function getScale () {
100214           return this._scale
100215         };
100216         PrecisionModel.prototype.isFloating = function isFloating () {
100217           return this._modelType === PrecisionModel.FLOATING || this._modelType === PrecisionModel.FLOATING_SINGLE
100218         };
100219         PrecisionModel.prototype.getType = function getType () {
100220           return this._modelType
100221         };
100222         PrecisionModel.prototype.toString = function toString () {
100223           var description = 'UNKNOWN';
100224           if (this._modelType === PrecisionModel.FLOATING) {
100225             description = 'Floating';
100226           } else if (this._modelType === PrecisionModel.FLOATING_SINGLE) {
100227             description = 'Floating-Single';
100228           } else if (this._modelType === PrecisionModel.FIXED) {
100229             description = 'Fixed (Scale=' + this.getScale() + ')';
100230           }
100231           return description
100232         };
100233         PrecisionModel.prototype.makePrecise = function makePrecise () {
100234           if (typeof arguments[0] === 'number') {
100235             var val = arguments[0];
100236             if (Double.isNaN(val)) { return val }
100237             if (this._modelType === PrecisionModel.FLOATING_SINGLE) {
100238               var floatSingleVal = val;
100239               return floatSingleVal
100240             }
100241             if (this._modelType === PrecisionModel.FIXED) {
100242               return Math.round(val * this._scale) / this._scale
100243             }
100244             return val
100245           } else if (arguments[0] instanceof Coordinate) {
100246             var coord = arguments[0];
100247             if (this._modelType === PrecisionModel.FLOATING) { return null }
100248             coord.x = this.makePrecise(coord.x);
100249             coord.y = this.makePrecise(coord.y);
100250           }
100251         };
100252         PrecisionModel.prototype.getMaximumSignificantDigits = function getMaximumSignificantDigits () {
100253           var maxSigDigits = 16;
100254           if (this._modelType === PrecisionModel.FLOATING) {
100255             maxSigDigits = 16;
100256           } else if (this._modelType === PrecisionModel.FLOATING_SINGLE) {
100257             maxSigDigits = 6;
100258           } else if (this._modelType === PrecisionModel.FIXED) {
100259             maxSigDigits = 1 + Math.trunc(Math.ceil(Math.log(this.getScale()) / Math.log(10)));
100260           }
100261           return maxSigDigits
100262         };
100263         PrecisionModel.prototype.setScale = function setScale (scale) {
100264           this._scale = Math.abs(scale);
100265         };
100266         PrecisionModel.prototype.interfaces_ = function interfaces_ () {
100267           return [Serializable, Comparable]
100268         };
100269         PrecisionModel.prototype.getClass = function getClass () {
100270           return PrecisionModel
100271         };
100272         PrecisionModel.mostPrecise = function mostPrecise (pm1, pm2) {
100273           if (pm1.compareTo(pm2) >= 0) { return pm1 }
100274           return pm2
100275         };
100276         staticAccessors$19.serialVersionUID.get = function () { return 7777263578777803835 };
100277         staticAccessors$19.maximumPreciseValue.get = function () { return 9007199254740992.0 };
100278
100279         Object.defineProperties( PrecisionModel, staticAccessors$19 );
100280
100281         var Type$2 = function Type (name) {
100282           this._name = name || null;
100283           Type.nameToTypeMap.put(name, this);
100284         };
100285
100286         var staticAccessors$1$1 = { serialVersionUID: { configurable: true },nameToTypeMap: { configurable: true } };
100287         Type$2.prototype.readResolve = function readResolve () {
100288           return Type$2.nameToTypeMap.get(this._name)
100289         };
100290         Type$2.prototype.toString = function toString () {
100291           return this._name
100292         };
100293         Type$2.prototype.interfaces_ = function interfaces_ () {
100294           return [Serializable]
100295         };
100296         Type$2.prototype.getClass = function getClass () {
100297           return Type$2
100298         };
100299         staticAccessors$1$1.serialVersionUID.get = function () { return -5528602631731589822 };
100300         staticAccessors$1$1.nameToTypeMap.get = function () { return new HashMap() };
100301
100302         Object.defineProperties( Type$2, staticAccessors$1$1 );
100303
100304         PrecisionModel.Type = Type$2;
100305         PrecisionModel.FIXED = new Type$2('FIXED');
100306         PrecisionModel.FLOATING = new Type$2('FLOATING');
100307         PrecisionModel.FLOATING_SINGLE = new Type$2('FLOATING SINGLE');
100308
100309         var GeometryFactory = function GeometryFactory () {
100310           this._precisionModel = new PrecisionModel();
100311           this._SRID = 0;
100312           this._coordinateSequenceFactory = GeometryFactory.getDefaultCoordinateSequenceFactory();
100313
100314           if (arguments.length === 0) ; else if (arguments.length === 1) {
100315             if (hasInterface(arguments[0], CoordinateSequenceFactory)) {
100316               this._coordinateSequenceFactory = arguments[0];
100317             } else if (arguments[0] instanceof PrecisionModel) {
100318               this._precisionModel = arguments[0];
100319             }
100320           } else if (arguments.length === 2) {
100321             this._precisionModel = arguments[0];
100322             this._SRID = arguments[1];
100323           } else if (arguments.length === 3) {
100324             this._precisionModel = arguments[0];
100325             this._SRID = arguments[1];
100326             this._coordinateSequenceFactory = arguments[2];
100327           }
100328         };
100329
100330         var staticAccessors$2 = { serialVersionUID: { configurable: true } };
100331         GeometryFactory.prototype.toGeometry = function toGeometry (envelope) {
100332           if (envelope.isNull()) {
100333             return this.createPoint(null)
100334           }
100335           if (envelope.getMinX() === envelope.getMaxX() && envelope.getMinY() === envelope.getMaxY()) {
100336             return this.createPoint(new Coordinate(envelope.getMinX(), envelope.getMinY()))
100337           }
100338           if (envelope.getMinX() === envelope.getMaxX() || envelope.getMinY() === envelope.getMaxY()) {
100339             return this.createLineString([new Coordinate(envelope.getMinX(), envelope.getMinY()), new Coordinate(envelope.getMaxX(), envelope.getMaxY())])
100340           }
100341           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)
100342         };
100343         GeometryFactory.prototype.createLineString = function createLineString (coordinates) {
100344           if (!coordinates) { return new LineString(this.getCoordinateSequenceFactory().create([]), this) }
100345           else if (coordinates instanceof Array) { return new LineString(this.getCoordinateSequenceFactory().create(coordinates), this) }
100346           else if (hasInterface(coordinates, CoordinateSequence)) { return new LineString(coordinates, this) }
100347         };
100348         GeometryFactory.prototype.createMultiLineString = function createMultiLineString () {
100349           if (arguments.length === 0) {
100350             return new MultiLineString(null, this)
100351           } else if (arguments.length === 1) {
100352             var lineStrings = arguments[0];
100353             return new MultiLineString(lineStrings, this)
100354           }
100355         };
100356         GeometryFactory.prototype.buildGeometry = function buildGeometry (geomList) {
100357           var geomClass = null;
100358           var isHeterogeneous = false;
100359           var hasGeometryCollection = false;
100360           for (var i = geomList.iterator(); i.hasNext();) {
100361             var geom = i.next();
100362             var partClass = geom.getClass();
100363             if (geomClass === null) {
100364               geomClass = partClass;
100365             }
100366             if (partClass !== geomClass) {
100367               isHeterogeneous = true;
100368             }
100369             if (geom.isGeometryCollectionOrDerived()) { hasGeometryCollection = true; }
100370           }
100371           if (geomClass === null) {
100372             return this.createGeometryCollection()
100373           }
100374           if (isHeterogeneous || hasGeometryCollection) {
100375             return this.createGeometryCollection(GeometryFactory.toGeometryArray(geomList))
100376           }
100377           var geom0 = geomList.iterator().next();
100378           var isCollection = geomList.size() > 1;
100379           if (isCollection) {
100380             if (geom0 instanceof Polygon) {
100381               return this.createMultiPolygon(GeometryFactory.toPolygonArray(geomList))
100382             } else if (geom0 instanceof LineString) {
100383               return this.createMultiLineString(GeometryFactory.toLineStringArray(geomList))
100384             } else if (geom0 instanceof Point$1) {
100385               return this.createMultiPoint(GeometryFactory.toPointArray(geomList))
100386             }
100387             Assert.shouldNeverReachHere('Unhandled class: ' + geom0.getClass().getName());
100388           }
100389           return geom0
100390         };
100391         GeometryFactory.prototype.createMultiPointFromCoords = function createMultiPointFromCoords (coordinates) {
100392           return this.createMultiPoint(coordinates !== null ? this.getCoordinateSequenceFactory().create(coordinates) : null)
100393         };
100394         GeometryFactory.prototype.createPoint = function createPoint () {
100395           if (arguments.length === 0) {
100396             return this.createPoint(this.getCoordinateSequenceFactory().create([]))
100397           } else if (arguments.length === 1) {
100398             if (arguments[0] instanceof Coordinate) {
100399               var coordinate = arguments[0];
100400               return this.createPoint(coordinate !== null ? this.getCoordinateSequenceFactory().create([coordinate]) : null)
100401             } else if (hasInterface(arguments[0], CoordinateSequence)) {
100402               var coordinates = arguments[0];
100403               return new Point$1(coordinates, this)
100404             }
100405           }
100406         };
100407         GeometryFactory.prototype.getCoordinateSequenceFactory = function getCoordinateSequenceFactory () {
100408           return this._coordinateSequenceFactory
100409         };
100410         GeometryFactory.prototype.createPolygon = function createPolygon () {
100411           if (arguments.length === 0) {
100412             return new Polygon(null, null, this)
100413           } else if (arguments.length === 1) {
100414             if (hasInterface(arguments[0], CoordinateSequence)) {
100415               var coordinates = arguments[0];
100416               return this.createPolygon(this.createLinearRing(coordinates))
100417             } else if (arguments[0] instanceof Array) {
100418               var coordinates$1 = arguments[0];
100419               return this.createPolygon(this.createLinearRing(coordinates$1))
100420             } else if (arguments[0] instanceof LinearRing) {
100421               var shell = arguments[0];
100422               return this.createPolygon(shell, null)
100423             }
100424           } else if (arguments.length === 2) {
100425             var shell$1 = arguments[0];
100426             var holes = arguments[1];
100427             return new Polygon(shell$1, holes, this)
100428           }
100429         };
100430         GeometryFactory.prototype.getSRID = function getSRID () {
100431           return this._SRID
100432         };
100433         GeometryFactory.prototype.createGeometryCollection = function createGeometryCollection () {
100434           if (arguments.length === 0) {
100435             return new GeometryCollection(null, this)
100436           } else if (arguments.length === 1) {
100437             var geometries = arguments[0];
100438             return new GeometryCollection(geometries, this)
100439           }
100440         };
100441         GeometryFactory.prototype.createGeometry = function createGeometry (g) {
100442           var editor = new GeometryEditor(this);
100443           return editor.edit(g, {
100444             edit: function () {
100445               if (arguments.length === 2) {
100446                 var coordSeq = arguments[0];
100447                 // const geometry = arguments[1]
100448                 return this._coordinateSequenceFactory.create(coordSeq)
100449               }
100450             }
100451           })
100452         };
100453         GeometryFactory.prototype.getPrecisionModel = function getPrecisionModel () {
100454           return this._precisionModel
100455         };
100456         GeometryFactory.prototype.createLinearRing = function createLinearRing () {
100457           if (arguments.length === 0) {
100458             return this.createLinearRing(this.getCoordinateSequenceFactory().create([]))
100459           } else if (arguments.length === 1) {
100460             if (arguments[0] instanceof Array) {
100461               var coordinates = arguments[0];
100462               return this.createLinearRing(coordinates !== null ? this.getCoordinateSequenceFactory().create(coordinates) : null)
100463             } else if (hasInterface(arguments[0], CoordinateSequence)) {
100464               var coordinates$1 = arguments[0];
100465               return new LinearRing(coordinates$1, this)
100466             }
100467           }
100468         };
100469         GeometryFactory.prototype.createMultiPolygon = function createMultiPolygon () {
100470           if (arguments.length === 0) {
100471             return new MultiPolygon(null, this)
100472           } else if (arguments.length === 1) {
100473             var polygons = arguments[0];
100474             return new MultiPolygon(polygons, this)
100475           }
100476         };
100477         GeometryFactory.prototype.createMultiPoint = function createMultiPoint () {
100478             var this$1 = this;
100479
100480           if (arguments.length === 0) {
100481             return new MultiPoint(null, this)
100482           } else if (arguments.length === 1) {
100483             if (arguments[0] instanceof Array) {
100484               var point = arguments[0];
100485               return new MultiPoint(point, this)
100486             } else if (arguments[0] instanceof Array) {
100487               var coordinates = arguments[0];
100488               return this.createMultiPoint(coordinates !== null ? this.getCoordinateSequenceFactory().create(coordinates) : null)
100489             } else if (hasInterface(arguments[0], CoordinateSequence)) {
100490               var coordinates$1 = arguments[0];
100491               if (coordinates$1 === null) {
100492                 return this.createMultiPoint(new Array(0).fill(null))
100493               }
100494               var points = new Array(coordinates$1.size()).fill(null);
100495               for (var i = 0; i < coordinates$1.size(); i++) {
100496                 var ptSeq = this$1.getCoordinateSequenceFactory().create(1, coordinates$1.getDimension());
100497                 CoordinateSequences.copy(coordinates$1, i, ptSeq, 0, 1);
100498                 points[i] = this$1.createPoint(ptSeq);
100499               }
100500               return this.createMultiPoint(points)
100501             }
100502           }
100503         };
100504         GeometryFactory.prototype.interfaces_ = function interfaces_ () {
100505           return [Serializable]
100506         };
100507         GeometryFactory.prototype.getClass = function getClass () {
100508           return GeometryFactory
100509         };
100510         GeometryFactory.toMultiPolygonArray = function toMultiPolygonArray (multiPolygons) {
100511           var multiPolygonArray = new Array(multiPolygons.size()).fill(null);
100512           return multiPolygons.toArray(multiPolygonArray)
100513         };
100514         GeometryFactory.toGeometryArray = function toGeometryArray (geometries) {
100515           if (geometries === null) { return null }
100516           var geometryArray = new Array(geometries.size()).fill(null);
100517           return geometries.toArray(geometryArray)
100518         };
100519         GeometryFactory.getDefaultCoordinateSequenceFactory = function getDefaultCoordinateSequenceFactory () {
100520           return CoordinateArraySequenceFactory.instance()
100521         };
100522         GeometryFactory.toMultiLineStringArray = function toMultiLineStringArray (multiLineStrings) {
100523           var multiLineStringArray = new Array(multiLineStrings.size()).fill(null);
100524           return multiLineStrings.toArray(multiLineStringArray)
100525         };
100526         GeometryFactory.toLineStringArray = function toLineStringArray (lineStrings) {
100527           var lineStringArray = new Array(lineStrings.size()).fill(null);
100528           return lineStrings.toArray(lineStringArray)
100529         };
100530         GeometryFactory.toMultiPointArray = function toMultiPointArray (multiPoints) {
100531           var multiPointArray = new Array(multiPoints.size()).fill(null);
100532           return multiPoints.toArray(multiPointArray)
100533         };
100534         GeometryFactory.toLinearRingArray = function toLinearRingArray (linearRings) {
100535           var linearRingArray = new Array(linearRings.size()).fill(null);
100536           return linearRings.toArray(linearRingArray)
100537         };
100538         GeometryFactory.toPointArray = function toPointArray (points) {
100539           var pointArray = new Array(points.size()).fill(null);
100540           return points.toArray(pointArray)
100541         };
100542         GeometryFactory.toPolygonArray = function toPolygonArray (polygons) {
100543           var polygonArray = new Array(polygons.size()).fill(null);
100544           return polygons.toArray(polygonArray)
100545         };
100546         GeometryFactory.createPointFromInternalCoord = function createPointFromInternalCoord (coord, exemplar) {
100547           exemplar.getPrecisionModel().makePrecise(coord);
100548           return exemplar.getFactory().createPoint(coord)
100549         };
100550         staticAccessors$2.serialVersionUID.get = function () { return -6820524753094095635 };
100551
100552         Object.defineProperties( GeometryFactory, staticAccessors$2 );
100553
100554         var geometryTypes = ['Point', 'MultiPoint', 'LineString', 'MultiLineString', 'Polygon', 'MultiPolygon'];
100555
100556         /**
100557          * Class for reading and writing Well-Known Text.Create a new parser for GeoJSON
100558          * NOTE: Adapted from OpenLayers 2.11 implementation.
100559          */
100560
100561         /**
100562          * Create a new parser for GeoJSON
100563          *
100564          * @param {GeometryFactory} geometryFactory
100565          * @return An instance of GeoJsonParser.
100566          * @constructor
100567          * @private
100568          */
100569         var GeoJSONParser = function GeoJSONParser (geometryFactory) {
100570           this.geometryFactory = geometryFactory || new GeometryFactory();
100571         };
100572         /**
100573          * Deserialize a GeoJSON object and return the Geometry or Feature(Collection) with JSTS Geometries
100574          *
100575          * @param {}
100576          *        A GeoJSON object.
100577          * @return {} A Geometry instance or object representing a Feature(Collection) with Geometry instances.
100578          * @private
100579          */
100580         GeoJSONParser.prototype.read = function read (json) {
100581           var obj;
100582           if (typeof json === 'string') {
100583             obj = JSON.parse(json);
100584           } else {
100585             obj = json;
100586           }
100587
100588           var type = obj.type;
100589
100590           if (!parse$2[type]) {
100591             throw new Error('Unknown GeoJSON type: ' + obj.type)
100592           }
100593
100594           if (geometryTypes.indexOf(type) !== -1) {
100595             return parse$2[type].apply(this, [obj.coordinates])
100596           } else if (type === 'GeometryCollection') {
100597             return parse$2[type].apply(this, [obj.geometries])
100598           }
100599
100600           // feature or feature collection
100601           return parse$2[type].apply(this, [obj])
100602         };
100603
100604         /**
100605          * Serialize a Geometry object into GeoJSON
100606          *
100607          * @param {Geometry}
100608          *        geometry A Geometry or array of Geometries.
100609          * @return {Object} A GeoJSON object represting the input Geometry/Geometries.
100610          * @private
100611          */
100612         GeoJSONParser.prototype.write = function write (geometry) {
100613           var type = geometry.getGeometryType();
100614
100615           if (!extract[type]) {
100616             throw new Error('Geometry is not supported')
100617           }
100618
100619           return extract[type].apply(this, [geometry])
100620         };
100621
100622         var parse$2 = {
100623           /**
100624            * Parse a GeoJSON Feature object
100625            *
100626            * @param {Object}
100627            *          obj Object to parse.
100628            *
100629            * @return {Object} Feature with geometry/bbox converted to JSTS Geometries.
100630            */
100631           Feature: function (obj) {
100632             var feature = {};
100633
100634             // copy features
100635             for (var key in obj) {
100636               feature[key] = obj[key];
100637             }
100638
100639             // parse geometry
100640             if (obj.geometry) {
100641               var type = obj.geometry.type;
100642               if (!parse$2[type]) {
100643                 throw new Error('Unknown GeoJSON type: ' + obj.type)
100644               }
100645               feature.geometry = this.read(obj.geometry);
100646             }
100647
100648             // bbox
100649             if (obj.bbox) {
100650               feature.bbox = parse$2.bbox.apply(this, [obj.bbox]);
100651             }
100652
100653             return feature
100654           },
100655
100656           /**
100657            * Parse a GeoJSON FeatureCollection object
100658            *
100659            * @param {Object}
100660            *          obj Object to parse.
100661            *
100662            * @return {Object} FeatureCollection with geometry/bbox converted to JSTS Geometries.
100663            */
100664           FeatureCollection: function (obj) {
100665             var this$1 = this;
100666
100667             var featureCollection = {};
100668
100669             if (obj.features) {
100670               featureCollection.features = [];
100671
100672               for (var i = 0; i < obj.features.length; ++i) {
100673                 featureCollection.features.push(this$1.read(obj.features[i]));
100674               }
100675             }
100676
100677             if (obj.bbox) {
100678               featureCollection.bbox = this.parse.bbox.apply(this, [obj.bbox]);
100679             }
100680
100681             return featureCollection
100682           },
100683
100684           /**
100685            * Convert the ordinates in an array to an array of Coordinates
100686            *
100687            * @param {Array}
100688            *          array Array with {Number}s.
100689            *
100690            * @return {Array} Array with Coordinates.
100691            */
100692           coordinates: function (array) {
100693             var coordinates = [];
100694             for (var i = 0; i < array.length; ++i) {
100695               var sub = array[i];
100696               coordinates.push(new Coordinate(sub[0], sub[1]));
100697             }
100698             return coordinates
100699           },
100700
100701           /**
100702            * Convert the bbox to a LinearRing
100703            *
100704            * @param {Array}
100705            *          array Array with [xMin, yMin, xMax, yMax].
100706            *
100707            * @return {Array} Array with Coordinates.
100708            */
100709           bbox: function (array) {
100710             return this.geometryFactory.createLinearRing([
100711               new Coordinate(array[0], array[1]),
100712               new Coordinate(array[2], array[1]),
100713               new Coordinate(array[2], array[3]),
100714               new Coordinate(array[0], array[3]),
100715               new Coordinate(array[0], array[1])
100716             ])
100717           },
100718
100719           /**
100720            * Convert an Array with ordinates to a Point
100721            *
100722            * @param {Array}
100723            *          array Array with ordinates.
100724            *
100725            * @return {Point} Point.
100726            */
100727           Point: function (array) {
100728             var coordinate = new Coordinate(array[0], array[1]);
100729             return this.geometryFactory.createPoint(coordinate)
100730           },
100731
100732           /**
100733            * Convert an Array with coordinates to a MultiPoint
100734            *
100735            * @param {Array}
100736            *          array Array with coordinates.
100737            *
100738            * @return {MultiPoint} MultiPoint.
100739            */
100740           MultiPoint: function (array) {
100741             var this$1 = this;
100742
100743             var points = [];
100744             for (var i = 0; i < array.length; ++i) {
100745               points.push(parse$2.Point.apply(this$1, [array[i]]));
100746             }
100747             return this.geometryFactory.createMultiPoint(points)
100748           },
100749
100750           /**
100751            * Convert an Array with coordinates to a LineString
100752            *
100753            * @param {Array}
100754            *          array Array with coordinates.
100755            *
100756            * @return {LineString} LineString.
100757            */
100758           LineString: function (array) {
100759             var coordinates = parse$2.coordinates.apply(this, [array]);
100760             return this.geometryFactory.createLineString(coordinates)
100761           },
100762
100763           /**
100764            * Convert an Array with coordinates to a MultiLineString
100765            *
100766            * @param {Array}
100767            *          array Array with coordinates.
100768            *
100769            * @return {MultiLineString} MultiLineString.
100770            */
100771           MultiLineString: function (array) {
100772             var this$1 = this;
100773
100774             var lineStrings = [];
100775             for (var i = 0; i < array.length; ++i) {
100776               lineStrings.push(parse$2.LineString.apply(this$1, [array[i]]));
100777             }
100778             return this.geometryFactory.createMultiLineString(lineStrings)
100779           },
100780
100781           /**
100782            * Convert an Array to a Polygon
100783            *
100784            * @param {Array}
100785            *          array Array with shell and holes.
100786            *
100787            * @return {Polygon} Polygon.
100788            */
100789           Polygon: function (array) {
100790             var this$1 = this;
100791
100792             var shellCoordinates = parse$2.coordinates.apply(this, [array[0]]);
100793             var shell = this.geometryFactory.createLinearRing(shellCoordinates);
100794             var holes = [];
100795             for (var i = 1; i < array.length; ++i) {
100796               var hole = array[i];
100797               var coordinates = parse$2.coordinates.apply(this$1, [hole]);
100798               var linearRing = this$1.geometryFactory.createLinearRing(coordinates);
100799               holes.push(linearRing);
100800             }
100801             return this.geometryFactory.createPolygon(shell, holes)
100802           },
100803
100804           /**
100805            * Convert an Array to a MultiPolygon
100806            *
100807            * @param {Array}
100808            *          array Array of arrays with shell and rings.
100809            *
100810            * @return {MultiPolygon} MultiPolygon.
100811            */
100812           MultiPolygon: function (array) {
100813             var this$1 = this;
100814
100815             var polygons = [];
100816             for (var i = 0; i < array.length; ++i) {
100817               var polygon = array[i];
100818               polygons.push(parse$2.Polygon.apply(this$1, [polygon]));
100819             }
100820             return this.geometryFactory.createMultiPolygon(polygons)
100821           },
100822
100823           /**
100824            * Convert an Array to a GeometryCollection
100825            *
100826            * @param {Array}
100827            *          array Array of GeoJSON geometries.
100828            *
100829            * @return {GeometryCollection} GeometryCollection.
100830            */
100831           GeometryCollection: function (array) {
100832             var this$1 = this;
100833
100834             var geometries = [];
100835             for (var i = 0; i < array.length; ++i) {
100836               var geometry = array[i];
100837               geometries.push(this$1.read(geometry));
100838             }
100839             return this.geometryFactory.createGeometryCollection(geometries)
100840           }
100841         };
100842
100843         var extract = {
100844           /**
100845            * Convert a Coordinate to an Array
100846            *
100847            * @param {Coordinate}
100848            *          coordinate Coordinate to convert.
100849            *
100850            * @return {Array} Array of ordinates.
100851            */
100852           coordinate: function (coordinate) {
100853             return [coordinate.x, coordinate.y]
100854           },
100855
100856           /**
100857            * Convert a Point to a GeoJSON object
100858            *
100859            * @param {Point}
100860            *          point Point to convert.
100861            *
100862            * @return {Array} Array of 2 ordinates (paired to a coordinate).
100863            */
100864           Point: function (point) {
100865             var array = extract.coordinate.apply(this, [point.getCoordinate()]);
100866             return {
100867               type: 'Point',
100868               coordinates: array
100869             }
100870           },
100871
100872           /**
100873            * Convert a MultiPoint to a GeoJSON object
100874            *
100875            * @param {MultiPoint}
100876            *          multipoint MultiPoint to convert.
100877            *
100878            * @return {Array} Array of coordinates.
100879            */
100880           MultiPoint: function (multipoint) {
100881             var this$1 = this;
100882
100883             var array = [];
100884             for (var i = 0; i < multipoint._geometries.length; ++i) {
100885               var point = multipoint._geometries[i];
100886               var geoJson = extract.Point.apply(this$1, [point]);
100887               array.push(geoJson.coordinates);
100888             }
100889             return {
100890               type: 'MultiPoint',
100891               coordinates: array
100892             }
100893           },
100894
100895           /**
100896            * Convert a LineString to a GeoJSON object
100897            *
100898            * @param {LineString}
100899            *          linestring LineString to convert.
100900            *
100901            * @return {Array} Array of coordinates.
100902            */
100903           LineString: function (linestring) {
100904             var this$1 = this;
100905
100906             var array = [];
100907             var coordinates = linestring.getCoordinates();
100908             for (var i = 0; i < coordinates.length; ++i) {
100909               var coordinate = coordinates[i];
100910               array.push(extract.coordinate.apply(this$1, [coordinate]));
100911             }
100912             return {
100913               type: 'LineString',
100914               coordinates: array
100915             }
100916           },
100917
100918           /**
100919            * Convert a MultiLineString to a GeoJSON object
100920            *
100921            * @param {MultiLineString}
100922            *          multilinestring MultiLineString to convert.
100923            *
100924            * @return {Array} Array of Array of coordinates.
100925            */
100926           MultiLineString: function (multilinestring) {
100927             var this$1 = this;
100928
100929             var array = [];
100930             for (var i = 0; i < multilinestring._geometries.length; ++i) {
100931               var linestring = multilinestring._geometries[i];
100932               var geoJson = extract.LineString.apply(this$1, [linestring]);
100933               array.push(geoJson.coordinates);
100934             }
100935             return {
100936               type: 'MultiLineString',
100937               coordinates: array
100938             }
100939           },
100940
100941           /**
100942            * Convert a Polygon to a GeoJSON object
100943            *
100944            * @param {Polygon}
100945            *          polygon Polygon to convert.
100946            *
100947            * @return {Array} Array with shell, holes.
100948            */
100949           Polygon: function (polygon) {
100950             var this$1 = this;
100951
100952             var array = [];
100953             var shellGeoJson = extract.LineString.apply(this, [polygon._shell]);
100954             array.push(shellGeoJson.coordinates);
100955             for (var i = 0; i < polygon._holes.length; ++i) {
100956               var hole = polygon._holes[i];
100957               var holeGeoJson = extract.LineString.apply(this$1, [hole]);
100958               array.push(holeGeoJson.coordinates);
100959             }
100960             return {
100961               type: 'Polygon',
100962               coordinates: array
100963             }
100964           },
100965
100966           /**
100967            * Convert a MultiPolygon to a GeoJSON object
100968            *
100969            * @param {MultiPolygon}
100970            *          multipolygon MultiPolygon to convert.
100971            *
100972            * @return {Array} Array of polygons.
100973            */
100974           MultiPolygon: function (multipolygon) {
100975             var this$1 = this;
100976
100977             var array = [];
100978             for (var i = 0; i < multipolygon._geometries.length; ++i) {
100979               var polygon = multipolygon._geometries[i];
100980               var geoJson = extract.Polygon.apply(this$1, [polygon]);
100981               array.push(geoJson.coordinates);
100982             }
100983             return {
100984               type: 'MultiPolygon',
100985               coordinates: array
100986             }
100987           },
100988
100989           /**
100990            * Convert a GeometryCollection to a GeoJSON object
100991            *
100992            * @param {GeometryCollection}
100993            *          collection GeometryCollection to convert.
100994            *
100995            * @return {Array} Array of geometries.
100996            */
100997           GeometryCollection: function (collection) {
100998             var this$1 = this;
100999
101000             var array = [];
101001             for (var i = 0; i < collection._geometries.length; ++i) {
101002               var geometry = collection._geometries[i];
101003               var type = geometry.getGeometryType();
101004               array.push(extract[type].apply(this$1, [geometry]));
101005             }
101006             return {
101007               type: 'GeometryCollection',
101008               geometries: array
101009             }
101010           }
101011         };
101012
101013         /**
101014          * Converts a geometry in GeoJSON to a {@link Geometry}.
101015          */
101016
101017         /**
101018          * A <code>GeoJSONReader</code> is parameterized by a <code>GeometryFactory</code>,
101019          * to allow it to create <code>Geometry</code> objects of the appropriate
101020          * implementation. In particular, the <code>GeometryFactory</code> determines
101021          * the <code>PrecisionModel</code> and <code>SRID</code> that is used.
101022          *
101023          * @param {GeometryFactory} geometryFactory
101024          * @constructor
101025          */
101026         var GeoJSONReader = function GeoJSONReader (geometryFactory) {
101027           this.geometryFactory = geometryFactory || new GeometryFactory();
101028           this.precisionModel = this.geometryFactory.getPrecisionModel();
101029           this.parser = new GeoJSONParser(this.geometryFactory);
101030         };
101031         /**
101032          * Reads a GeoJSON representation of a {@link Geometry}
101033          *
101034          * Will also parse GeoJSON Features/FeatureCollections as custom objects.
101035          *
101036          * @param {Object|String} geoJson a GeoJSON Object or String.
101037          * @return {Geometry|Object} a <code>Geometry or Feature/FeatureCollection representation.</code>
101038          * @memberof GeoJSONReader
101039          */
101040         GeoJSONReader.prototype.read = function read (geoJson) {
101041           var geometry = this.parser.read(geoJson);
101042
101043           if (this.precisionModel.getType() === PrecisionModel.FIXED) {
101044             this.reducePrecision(geometry);
101045           }
101046
101047           return geometry
101048         };
101049
101050         // NOTE: this is a hack
101051         GeoJSONReader.prototype.reducePrecision = function reducePrecision (geometry) {
101052             var this$1 = this;
101053
101054           var i, len;
101055
101056           if (geometry.coordinate) {
101057             this.precisionModel.makePrecise(geometry.coordinate);
101058           } else if (geometry.points) {
101059             for (i = 0, len = geometry.points.length; i < len; i++) {
101060               this$1.precisionModel.makePrecise(geometry.points[i]);
101061             }
101062           } else if (geometry.geometries) {
101063             for (i = 0, len = geometry.geometries.length; i < len; i++) {
101064               this$1.reducePrecision(geometry.geometries[i]);
101065             }
101066           }
101067         };
101068
101069         /**
101070          * @module GeoJSONWriter
101071          */
101072
101073         /**
101074          * Writes the GeoJSON representation of a {@link Geometry}. The
101075          * The GeoJSON format is defined <A
101076          * HREF="http://geojson.org/geojson-spec.html">here</A>.
101077          */
101078
101079         /**
101080          * The <code>GeoJSONWriter</code> outputs coordinates rounded to the precision
101081          * model. Only the maximum number of decimal places necessary to represent the
101082          * ordinates to the required precision will be output.
101083          *
101084          * @param {GeometryFactory} geometryFactory
101085          * @constructor
101086          */
101087         var GeoJSONWriter = function GeoJSONWriter () {
101088           this.parser = new GeoJSONParser(this.geometryFactory);
101089         };
101090         /**
101091          * Converts a <code>Geometry</code> to its GeoJSON representation.
101092          *
101093          * @param {Geometry}
101094          *        geometry a <code>Geometry</code> to process.
101095          * @return {Object} The GeoJSON representation of the Geometry.
101096          * @memberof GeoJSONWriter
101097          */
101098         GeoJSONWriter.prototype.write = function write (geometry) {
101099           return this.parser.write(geometry)
101100         };
101101
101102         /* eslint-disable no-undef */
101103
101104         // io
101105
101106         var Position = function Position () {};
101107
101108         var staticAccessors$20 = { ON: { configurable: true },LEFT: { configurable: true },RIGHT: { configurable: true } };
101109
101110         Position.prototype.interfaces_ = function interfaces_ () {
101111           return []
101112         };
101113         Position.prototype.getClass = function getClass () {
101114           return Position
101115         };
101116         Position.opposite = function opposite (position) {
101117           if (position === Position.LEFT) { return Position.RIGHT }
101118           if (position === Position.RIGHT) { return Position.LEFT }
101119           return position
101120         };
101121         staticAccessors$20.ON.get = function () { return 0 };
101122         staticAccessors$20.LEFT.get = function () { return 1 };
101123         staticAccessors$20.RIGHT.get = function () { return 2 };
101124
101125         Object.defineProperties( Position, staticAccessors$20 );
101126
101127         /**
101128          * @param {string=} message Optional message
101129          * @extends {Error}
101130          * @constructor
101131          * @private
101132          */
101133         function EmptyStackException (message) {
101134           this.message = message || '';
101135         }
101136         EmptyStackException.prototype = new Error();
101137
101138         /**
101139          * @type {string}
101140          */
101141         EmptyStackException.prototype.name = 'EmptyStackException';
101142
101143         /**
101144          * @see http://download.oracle.com/javase/6/docs/api/java/util/Stack.html
101145          *
101146          * @extends {List}
101147          * @constructor
101148          * @private
101149          */
101150         function Stack () {
101151           /**
101152            * @type {Array}
101153            * @private
101154            */
101155           this.array_ = [];
101156         }
101157         Stack.prototype = new List();
101158
101159         /**
101160          * @override
101161          */
101162         Stack.prototype.add = function (e) {
101163           this.array_.push(e);
101164           return true
101165         };
101166
101167         /**
101168          * @override
101169          */
101170         Stack.prototype.get = function (index) {
101171           if (index < 0 || index >= this.size()) {
101172             throw new Error()
101173           }
101174
101175           return this.array_[index]
101176         };
101177
101178         /**
101179          * Pushes an item onto the top of this stack.
101180          * @param {Object} e
101181          * @return {Object}
101182          */
101183         Stack.prototype.push = function (e) {
101184           this.array_.push(e);
101185           return e
101186         };
101187
101188         /**
101189          * Pushes an item onto the top of this stack.
101190          * @param {Object} e
101191          * @return {Object}
101192          */
101193         Stack.prototype.pop = function (e) {
101194           if (this.array_.length === 0) {
101195             throw new EmptyStackException()
101196           }
101197
101198           return this.array_.pop()
101199         };
101200
101201         /**
101202          * Looks at the object at the top of this stack without removing it from the
101203          * stack.
101204          * @return {Object}
101205          */
101206         Stack.prototype.peek = function () {
101207           if (this.array_.length === 0) {
101208             throw new EmptyStackException()
101209           }
101210
101211           return this.array_[this.array_.length - 1]
101212         };
101213
101214         /**
101215          * Tests if this stack is empty.
101216          * @return {boolean} true if and only if this stack contains no items; false
101217          *         otherwise.
101218          */
101219         Stack.prototype.empty = function () {
101220           if (this.array_.length === 0) {
101221             return true
101222           } else {
101223             return false
101224           }
101225         };
101226
101227         /**
101228          * @return {boolean}
101229          */
101230         Stack.prototype.isEmpty = function () {
101231           return this.empty()
101232         };
101233
101234         /**
101235          * Returns the 1-based position where an object is on this stack. If the object
101236          * o occurs as an item in this stack, this method returns the distance from the
101237          * top of the stack of the occurrence nearest the top of the stack; the topmost
101238          * item on the stack is considered to be at distance 1. The equals method is
101239          * used to compare o to the items in this stack.
101240          *
101241          * NOTE: does not currently actually use equals. (=== is used)
101242          *
101243          * @param {Object} o
101244          * @return {number} the 1-based position from the top of the stack where the
101245          *         object is located; the return value -1 indicates that the object is
101246          *         not on the stack.
101247          */
101248         Stack.prototype.search = function (o) {
101249           return this.array_.indexOf(o)
101250         };
101251
101252         /**
101253          * @return {number}
101254          * @export
101255          */
101256         Stack.prototype.size = function () {
101257           return this.array_.length
101258         };
101259
101260         /**
101261          * @return {Array}
101262          */
101263         Stack.prototype.toArray = function () {
101264           var this$1 = this;
101265
101266           var array = [];
101267
101268           for (var i = 0, len = this.array_.length; i < len; i++) {
101269             array.push(this$1.array_[i]);
101270           }
101271
101272           return array
101273         };
101274
101275         var RightmostEdgeFinder = function RightmostEdgeFinder () {
101276           this._minIndex = -1;
101277           this._minCoord = null;
101278           this._minDe = null;
101279           this._orientedDe = null;
101280         };
101281         RightmostEdgeFinder.prototype.getCoordinate = function getCoordinate () {
101282           return this._minCoord
101283         };
101284         RightmostEdgeFinder.prototype.getRightmostSide = function getRightmostSide (de, index) {
101285           var side = this.getRightmostSideOfSegment(de, index);
101286           if (side < 0) { side = this.getRightmostSideOfSegment(de, index - 1); }
101287           if (side < 0) {
101288             this._minCoord = null;
101289             this.checkForRightmostCoordinate(de);
101290           }
101291           return side
101292         };
101293         RightmostEdgeFinder.prototype.findRightmostEdgeAtVertex = function findRightmostEdgeAtVertex () {
101294           var pts = this._minDe.getEdge().getCoordinates();
101295           Assert.isTrue(this._minIndex > 0 && this._minIndex < pts.length, 'rightmost point expected to be interior vertex of edge');
101296           var pPrev = pts[this._minIndex - 1];
101297           var pNext = pts[this._minIndex + 1];
101298           var orientation = CGAlgorithms.computeOrientation(this._minCoord, pNext, pPrev);
101299           var usePrev = false;
101300           if (pPrev.y < this._minCoord.y && pNext.y < this._minCoord.y && orientation === CGAlgorithms.COUNTERCLOCKWISE) {
101301             usePrev = true;
101302           } else if (pPrev.y > this._minCoord.y && pNext.y > this._minCoord.y && orientation === CGAlgorithms.CLOCKWISE) {
101303             usePrev = true;
101304           }
101305           if (usePrev) {
101306             this._minIndex = this._minIndex - 1;
101307           }
101308         };
101309         RightmostEdgeFinder.prototype.getRightmostSideOfSegment = function getRightmostSideOfSegment (de, i) {
101310           var e = de.getEdge();
101311           var coord = e.getCoordinates();
101312           if (i < 0 || i + 1 >= coord.length) { return -1 }
101313           if (coord[i].y === coord[i + 1].y) { return -1 }
101314           var pos = Position.LEFT;
101315           if (coord[i].y < coord[i + 1].y) { pos = Position.RIGHT; }
101316           return pos
101317         };
101318         RightmostEdgeFinder.prototype.getEdge = function getEdge () {
101319           return this._orientedDe
101320         };
101321         RightmostEdgeFinder.prototype.checkForRightmostCoordinate = function checkForRightmostCoordinate (de) {
101322             var this$1 = this;
101323
101324           var coord = de.getEdge().getCoordinates();
101325           for (var i = 0; i < coord.length - 1; i++) {
101326             if (this$1._minCoord === null || coord[i].x > this$1._minCoord.x) {
101327               this$1._minDe = de;
101328               this$1._minIndex = i;
101329               this$1._minCoord = coord[i];
101330             }
101331           }
101332         };
101333         RightmostEdgeFinder.prototype.findRightmostEdgeAtNode = function findRightmostEdgeAtNode () {
101334           var node = this._minDe.getNode();
101335           var star = node.getEdges();
101336           this._minDe = star.getRightmostEdge();
101337           if (!this._minDe.isForward()) {
101338             this._minDe = this._minDe.getSym();
101339             this._minIndex = this._minDe.getEdge().getCoordinates().length - 1;
101340           }
101341         };
101342         RightmostEdgeFinder.prototype.findEdge = function findEdge (dirEdgeList) {
101343             var this$1 = this;
101344
101345           for (var i = dirEdgeList.iterator(); i.hasNext();) {
101346             var de = i.next();
101347             if (!de.isForward()) { continue }
101348             this$1.checkForRightmostCoordinate(de);
101349           }
101350           Assert.isTrue(this._minIndex !== 0 || this._minCoord.equals(this._minDe.getCoordinate()), 'inconsistency in rightmost processing');
101351           if (this._minIndex === 0) {
101352             this.findRightmostEdgeAtNode();
101353           } else {
101354             this.findRightmostEdgeAtVertex();
101355           }
101356           this._orientedDe = this._minDe;
101357           var rightmostSide = this.getRightmostSide(this._minDe, this._minIndex);
101358           if (rightmostSide === Position.LEFT) {
101359             this._orientedDe = this._minDe.getSym();
101360           }
101361         };
101362         RightmostEdgeFinder.prototype.interfaces_ = function interfaces_ () {
101363           return []
101364         };
101365         RightmostEdgeFinder.prototype.getClass = function getClass () {
101366           return RightmostEdgeFinder
101367         };
101368
101369         var TopologyException = (function (RuntimeException$$1) {
101370           function TopologyException (msg, pt) {
101371             RuntimeException$$1.call(this, TopologyException.msgWithCoord(msg, pt));
101372             this.pt = pt ? new Coordinate(pt) : null;
101373             this.name = 'TopologyException';
101374           }
101375
101376           if ( RuntimeException$$1 ) { TopologyException.__proto__ = RuntimeException$$1; }
101377           TopologyException.prototype = Object.create( RuntimeException$$1 && RuntimeException$$1.prototype );
101378           TopologyException.prototype.constructor = TopologyException;
101379           TopologyException.prototype.getCoordinate = function getCoordinate () {
101380             return this.pt
101381           };
101382           TopologyException.prototype.interfaces_ = function interfaces_ () {
101383             return []
101384           };
101385           TopologyException.prototype.getClass = function getClass () {
101386             return TopologyException
101387           };
101388           TopologyException.msgWithCoord = function msgWithCoord (msg, pt) {
101389             if (!pt) { return msg + ' [ ' + pt + ' ]' }
101390             return msg
101391           };
101392
101393           return TopologyException;
101394         }(RuntimeException));
101395
101396         var LinkedList = function LinkedList () {
101397           this.array_ = [];
101398         };
101399         LinkedList.prototype.addLast = function addLast (e) {
101400           this.array_.push(e);
101401         };
101402         LinkedList.prototype.removeFirst = function removeFirst () {
101403           return this.array_.shift()
101404         };
101405         LinkedList.prototype.isEmpty = function isEmpty () {
101406           return this.array_.length === 0
101407         };
101408
101409         var BufferSubgraph = function BufferSubgraph () {
101410           this._finder = null;
101411           this._dirEdgeList = new ArrayList();
101412           this._nodes = new ArrayList();
101413           this._rightMostCoord = null;
101414           this._env = null;
101415           this._finder = new RightmostEdgeFinder();
101416         };
101417         BufferSubgraph.prototype.clearVisitedEdges = function clearVisitedEdges () {
101418           for (var it = this._dirEdgeList.iterator(); it.hasNext();) {
101419             var de = it.next();
101420             de.setVisited(false);
101421           }
101422         };
101423         BufferSubgraph.prototype.getRightmostCoordinate = function getRightmostCoordinate () {
101424           return this._rightMostCoord
101425         };
101426         BufferSubgraph.prototype.computeNodeDepth = function computeNodeDepth (n) {
101427             var this$1 = this;
101428
101429           var startEdge = null;
101430           for (var i = n.getEdges().iterator(); i.hasNext();) {
101431             var de = i.next();
101432             if (de.isVisited() || de.getSym().isVisited()) {
101433               startEdge = de;
101434               break
101435             }
101436           }
101437           if (startEdge === null) { throw new TopologyException('unable to find edge to compute depths at ' + n.getCoordinate()) }
101438           n.getEdges().computeDepths(startEdge);
101439           for (var i$1 = n.getEdges().iterator(); i$1.hasNext();) {
101440             var de$1 = i$1.next();
101441             de$1.setVisited(true);
101442             this$1.copySymDepths(de$1);
101443           }
101444         };
101445         BufferSubgraph.prototype.computeDepth = function computeDepth (outsideDepth) {
101446           this.clearVisitedEdges();
101447           var de = this._finder.getEdge();
101448           // const n = de.getNode()
101449           // const label = de.getLabel()
101450           de.setEdgeDepths(Position.RIGHT, outsideDepth);
101451           this.copySymDepths(de);
101452           this.computeDepths(de);
101453         };
101454         BufferSubgraph.prototype.create = function create (node) {
101455           this.addReachable(node);
101456           this._finder.findEdge(this._dirEdgeList);
101457           this._rightMostCoord = this._finder.getCoordinate();
101458         };
101459         BufferSubgraph.prototype.findResultEdges = function findResultEdges () {
101460           for (var it = this._dirEdgeList.iterator(); it.hasNext();) {
101461             var de = it.next();
101462             if (de.getDepth(Position.RIGHT) >= 1 && de.getDepth(Position.LEFT) <= 0 && !de.isInteriorAreaEdge()) {
101463               de.setInResult(true);
101464             }
101465           }
101466         };
101467         BufferSubgraph.prototype.computeDepths = function computeDepths (startEdge) {
101468             var this$1 = this;
101469
101470           var nodesVisited = new HashSet();
101471           var nodeQueue = new LinkedList();
101472           var startNode = startEdge.getNode();
101473           nodeQueue.addLast(startNode);
101474           nodesVisited.add(startNode);
101475           startEdge.setVisited(true);
101476           while (!nodeQueue.isEmpty()) {
101477             var n = nodeQueue.removeFirst();
101478             nodesVisited.add(n);
101479             this$1.computeNodeDepth(n);
101480             for (var i = n.getEdges().iterator(); i.hasNext();) {
101481               var de = i.next();
101482               var sym = de.getSym();
101483               if (sym.isVisited()) { continue }
101484               var adjNode = sym.getNode();
101485               if (!nodesVisited.contains(adjNode)) {
101486                 nodeQueue.addLast(adjNode);
101487                 nodesVisited.add(adjNode);
101488               }
101489             }
101490           }
101491         };
101492         BufferSubgraph.prototype.compareTo = function compareTo (o) {
101493           var graph = o;
101494           if (this._rightMostCoord.x < graph._rightMostCoord.x) {
101495             return -1
101496           }
101497           if (this._rightMostCoord.x > graph._rightMostCoord.x) {
101498             return 1
101499           }
101500           return 0
101501         };
101502         BufferSubgraph.prototype.getEnvelope = function getEnvelope () {
101503           if (this._env === null) {
101504             var edgeEnv = new Envelope();
101505             for (var it = this._dirEdgeList.iterator(); it.hasNext();) {
101506               var dirEdge = it.next();
101507               var pts = dirEdge.getEdge().getCoordinates();
101508               for (var i = 0; i < pts.length - 1; i++) {
101509                 edgeEnv.expandToInclude(pts[i]);
101510               }
101511             }
101512             this._env = edgeEnv;
101513           }
101514           return this._env
101515         };
101516         BufferSubgraph.prototype.addReachable = function addReachable (startNode) {
101517             var this$1 = this;
101518
101519           var nodeStack = new Stack();
101520           nodeStack.add(startNode);
101521           while (!nodeStack.empty()) {
101522             var node = nodeStack.pop();
101523             this$1.add(node, nodeStack);
101524           }
101525         };
101526         BufferSubgraph.prototype.copySymDepths = function copySymDepths (de) {
101527           var sym = de.getSym();
101528           sym.setDepth(Position.LEFT, de.getDepth(Position.RIGHT));
101529           sym.setDepth(Position.RIGHT, de.getDepth(Position.LEFT));
101530         };
101531         BufferSubgraph.prototype.add = function add (node, nodeStack) {
101532             var this$1 = this;
101533
101534           node.setVisited(true);
101535           this._nodes.add(node);
101536           for (var i = node.getEdges().iterator(); i.hasNext();) {
101537             var de = i.next();
101538             this$1._dirEdgeList.add(de);
101539             var sym = de.getSym();
101540             var symNode = sym.getNode();
101541             if (!symNode.isVisited()) { nodeStack.push(symNode); }
101542           }
101543         };
101544         BufferSubgraph.prototype.getNodes = function getNodes () {
101545           return this._nodes
101546         };
101547         BufferSubgraph.prototype.getDirectedEdges = function getDirectedEdges () {
101548           return this._dirEdgeList
101549         };
101550         BufferSubgraph.prototype.interfaces_ = function interfaces_ () {
101551           return [Comparable]
101552         };
101553         BufferSubgraph.prototype.getClass = function getClass () {
101554           return BufferSubgraph
101555         };
101556
101557         var TopologyLocation = function TopologyLocation () {
101558           var this$1 = this;
101559
101560           this.location = null;
101561           if (arguments.length === 1) {
101562             if (arguments[0] instanceof Array) {
101563               var location = arguments[0];
101564               this.init(location.length);
101565             } else if (Number.isInteger(arguments[0])) {
101566               var on = arguments[0];
101567               this.init(1);
101568               this.location[Position.ON] = on;
101569             } else if (arguments[0] instanceof TopologyLocation) {
101570               var gl = arguments[0];
101571               this.init(gl.location.length);
101572               if (gl !== null) {
101573                 for (var i = 0; i < this.location.length; i++) {
101574                   this$1.location[i] = gl.location[i];
101575                 }
101576               }
101577             }
101578           } else if (arguments.length === 3) {
101579             var on$1 = arguments[0];
101580             var left = arguments[1];
101581             var right = arguments[2];
101582             this.init(3);
101583             this.location[Position.ON] = on$1;
101584             this.location[Position.LEFT] = left;
101585             this.location[Position.RIGHT] = right;
101586           }
101587         };
101588         TopologyLocation.prototype.setAllLocations = function setAllLocations (locValue) {
101589             var this$1 = this;
101590
101591           for (var i = 0; i < this.location.length; i++) {
101592             this$1.location[i] = locValue;
101593           }
101594         };
101595         TopologyLocation.prototype.isNull = function isNull () {
101596             var this$1 = this;
101597
101598           for (var i = 0; i < this.location.length; i++) {
101599             if (this$1.location[i] !== Location.NONE) { return false }
101600           }
101601           return true
101602         };
101603         TopologyLocation.prototype.setAllLocationsIfNull = function setAllLocationsIfNull (locValue) {
101604             var this$1 = this;
101605
101606           for (var i = 0; i < this.location.length; i++) {
101607             if (this$1.location[i] === Location.NONE) { this$1.location[i] = locValue; }
101608           }
101609         };
101610         TopologyLocation.prototype.isLine = function isLine () {
101611           return this.location.length === 1
101612         };
101613         TopologyLocation.prototype.merge = function merge (gl) {
101614             var this$1 = this;
101615
101616           if (gl.location.length > this.location.length) {
101617             var newLoc = new Array(3).fill(null);
101618             newLoc[Position.ON] = this.location[Position.ON];
101619             newLoc[Position.LEFT] = Location.NONE;
101620             newLoc[Position.RIGHT] = Location.NONE;
101621             this.location = newLoc;
101622           }
101623           for (var i = 0; i < this.location.length; i++) {
101624             if (this$1.location[i] === Location.NONE && i < gl.location.length) { this$1.location[i] = gl.location[i]; }
101625           }
101626         };
101627         TopologyLocation.prototype.getLocations = function getLocations () {
101628           return this.location
101629         };
101630         TopologyLocation.prototype.flip = function flip () {
101631           if (this.location.length <= 1) { return null }
101632           var temp = this.location[Position.LEFT];
101633           this.location[Position.LEFT] = this.location[Position.RIGHT];
101634           this.location[Position.RIGHT] = temp;
101635         };
101636         TopologyLocation.prototype.toString = function toString () {
101637           var buf = new StringBuffer();
101638           if (this.location.length > 1) { buf.append(Location.toLocationSymbol(this.location[Position.LEFT])); }
101639           buf.append(Location.toLocationSymbol(this.location[Position.ON]));
101640           if (this.location.length > 1) { buf.append(Location.toLocationSymbol(this.location[Position.RIGHT])); }
101641           return buf.toString()
101642         };
101643         TopologyLocation.prototype.setLocations = function setLocations (on, left, right) {
101644           this.location[Position.ON] = on;
101645           this.location[Position.LEFT] = left;
101646           this.location[Position.RIGHT] = right;
101647         };
101648         TopologyLocation.prototype.get = function get (posIndex) {
101649           if (posIndex < this.location.length) { return this.location[posIndex] }
101650           return Location.NONE
101651         };
101652         TopologyLocation.prototype.isArea = function isArea () {
101653           return this.location.length > 1
101654         };
101655         TopologyLocation.prototype.isAnyNull = function isAnyNull () {
101656             var this$1 = this;
101657
101658           for (var i = 0; i < this.location.length; i++) {
101659             if (this$1.location[i] === Location.NONE) { return true }
101660           }
101661           return false
101662         };
101663         TopologyLocation.prototype.setLocation = function setLocation () {
101664           if (arguments.length === 1) {
101665             var locValue = arguments[0];
101666             this.setLocation(Position.ON, locValue);
101667           } else if (arguments.length === 2) {
101668             var locIndex = arguments[0];
101669             var locValue$1 = arguments[1];
101670             this.location[locIndex] = locValue$1;
101671           }
101672         };
101673         TopologyLocation.prototype.init = function init (size) {
101674           this.location = new Array(size).fill(null);
101675           this.setAllLocations(Location.NONE);
101676         };
101677         TopologyLocation.prototype.isEqualOnSide = function isEqualOnSide (le, locIndex) {
101678           return this.location[locIndex] === le.location[locIndex]
101679         };
101680         TopologyLocation.prototype.allPositionsEqual = function allPositionsEqual (loc) {
101681             var this$1 = this;
101682
101683           for (var i = 0; i < this.location.length; i++) {
101684             if (this$1.location[i] !== loc) { return false }
101685           }
101686           return true
101687         };
101688         TopologyLocation.prototype.interfaces_ = function interfaces_ () {
101689           return []
101690         };
101691         TopologyLocation.prototype.getClass = function getClass () {
101692           return TopologyLocation
101693         };
101694
101695         var Label = function Label () {
101696           this.elt = new Array(2).fill(null);
101697           if (arguments.length === 1) {
101698             if (Number.isInteger(arguments[0])) {
101699               var onLoc = arguments[0];
101700               this.elt[0] = new TopologyLocation(onLoc);
101701               this.elt[1] = new TopologyLocation(onLoc);
101702             } else if (arguments[0] instanceof Label) {
101703               var lbl = arguments[0];
101704               this.elt[0] = new TopologyLocation(lbl.elt[0]);
101705               this.elt[1] = new TopologyLocation(lbl.elt[1]);
101706             }
101707           } else if (arguments.length === 2) {
101708             var geomIndex = arguments[0];
101709             var onLoc$1 = arguments[1];
101710             this.elt[0] = new TopologyLocation(Location.NONE);
101711             this.elt[1] = new TopologyLocation(Location.NONE);
101712             this.elt[geomIndex].setLocation(onLoc$1);
101713           } else if (arguments.length === 3) {
101714             var onLoc$2 = arguments[0];
101715             var leftLoc = arguments[1];
101716             var rightLoc = arguments[2];
101717             this.elt[0] = new TopologyLocation(onLoc$2, leftLoc, rightLoc);
101718             this.elt[1] = new TopologyLocation(onLoc$2, leftLoc, rightLoc);
101719           } else if (arguments.length === 4) {
101720             var geomIndex$1 = arguments[0];
101721             var onLoc$3 = arguments[1];
101722             var leftLoc$1 = arguments[2];
101723             var rightLoc$1 = arguments[3];
101724             this.elt[0] = new TopologyLocation(Location.NONE, Location.NONE, Location.NONE);
101725             this.elt[1] = new TopologyLocation(Location.NONE, Location.NONE, Location.NONE);
101726             this.elt[geomIndex$1].setLocations(onLoc$3, leftLoc$1, rightLoc$1);
101727           }
101728         };
101729         Label.prototype.getGeometryCount = function getGeometryCount () {
101730           var count = 0;
101731           if (!this.elt[0].isNull()) { count++; }
101732           if (!this.elt[1].isNull()) { count++; }
101733           return count
101734         };
101735         Label.prototype.setAllLocations = function setAllLocations (geomIndex, location) {
101736           this.elt[geomIndex].setAllLocations(location);
101737         };
101738         Label.prototype.isNull = function isNull (geomIndex) {
101739           return this.elt[geomIndex].isNull()
101740         };
101741         Label.prototype.setAllLocationsIfNull = function setAllLocationsIfNull () {
101742           if (arguments.length === 1) {
101743             var location = arguments[0];
101744             this.setAllLocationsIfNull(0, location);
101745             this.setAllLocationsIfNull(1, location);
101746           } else if (arguments.length === 2) {
101747             var geomIndex = arguments[0];
101748             var location$1 = arguments[1];
101749             this.elt[geomIndex].setAllLocationsIfNull(location$1);
101750           }
101751         };
101752         Label.prototype.isLine = function isLine (geomIndex) {
101753           return this.elt[geomIndex].isLine()
101754         };
101755         Label.prototype.merge = function merge (lbl) {
101756             var this$1 = this;
101757
101758           for (var i = 0; i < 2; i++) {
101759             if (this$1.elt[i] === null && lbl.elt[i] !== null) {
101760               this$1.elt[i] = new TopologyLocation(lbl.elt[i]);
101761             } else {
101762               this$1.elt[i].merge(lbl.elt[i]);
101763             }
101764           }
101765         };
101766         Label.prototype.flip = function flip () {
101767           this.elt[0].flip();
101768           this.elt[1].flip();
101769         };
101770         Label.prototype.getLocation = function getLocation () {
101771           if (arguments.length === 1) {
101772             var geomIndex = arguments[0];
101773             return this.elt[geomIndex].get(Position.ON)
101774           } else if (arguments.length === 2) {
101775             var geomIndex$1 = arguments[0];
101776             var posIndex = arguments[1];
101777             return this.elt[geomIndex$1].get(posIndex)
101778           }
101779         };
101780         Label.prototype.toString = function toString () {
101781           var buf = new StringBuffer();
101782           if (this.elt[0] !== null) {
101783             buf.append('A:');
101784             buf.append(this.elt[0].toString());
101785           }
101786           if (this.elt[1] !== null) {
101787             buf.append(' B:');
101788             buf.append(this.elt[1].toString());
101789           }
101790           return buf.toString()
101791         };
101792         Label.prototype.isArea = function isArea () {
101793           if (arguments.length === 0) {
101794             return this.elt[0].isArea() || this.elt[1].isArea()
101795           } else if (arguments.length === 1) {
101796             var geomIndex = arguments[0];
101797             return this.elt[geomIndex].isArea()
101798           }
101799         };
101800         Label.prototype.isAnyNull = function isAnyNull (geomIndex) {
101801           return this.elt[geomIndex].isAnyNull()
101802         };
101803         Label.prototype.setLocation = function setLocation () {
101804           if (arguments.length === 2) {
101805             var geomIndex = arguments[0];
101806             var location = arguments[1];
101807             this.elt[geomIndex].setLocation(Position.ON, location);
101808           } else if (arguments.length === 3) {
101809             var geomIndex$1 = arguments[0];
101810             var posIndex = arguments[1];
101811             var location$1 = arguments[2];
101812             this.elt[geomIndex$1].setLocation(posIndex, location$1);
101813           }
101814         };
101815         Label.prototype.isEqualOnSide = function isEqualOnSide (lbl, side) {
101816           return this.elt[0].isEqualOnSide(lbl.elt[0], side) && this.elt[1].isEqualOnSide(lbl.elt[1], side)
101817         };
101818         Label.prototype.allPositionsEqual = function allPositionsEqual (geomIndex, loc) {
101819           return this.elt[geomIndex].allPositionsEqual(loc)
101820         };
101821         Label.prototype.toLine = function toLine (geomIndex) {
101822           if (this.elt[geomIndex].isArea()) { this.elt[geomIndex] = new TopologyLocation(this.elt[geomIndex].location[0]); }
101823         };
101824         Label.prototype.interfaces_ = function interfaces_ () {
101825           return []
101826         };
101827         Label.prototype.getClass = function getClass () {
101828           return Label
101829         };
101830         Label.toLineLabel = function toLineLabel (label) {
101831           var lineLabel = new Label(Location.NONE);
101832           for (var i = 0; i < 2; i++) {
101833             lineLabel.setLocation(i, label.getLocation(i));
101834           }
101835           return lineLabel
101836         };
101837
101838         var EdgeRing = function EdgeRing () {
101839           this._startDe = null;
101840           this._maxNodeDegree = -1;
101841           this._edges = new ArrayList();
101842           this._pts = new ArrayList();
101843           this._label = new Label(Location.NONE);
101844           this._ring = null;
101845           this._isHole = null;
101846           this._shell = null;
101847           this._holes = new ArrayList();
101848           this._geometryFactory = null;
101849           var start = arguments[0];
101850           var geometryFactory = arguments[1];
101851           this._geometryFactory = geometryFactory;
101852           this.computePoints(start);
101853           this.computeRing();
101854         };
101855         EdgeRing.prototype.computeRing = function computeRing () {
101856             var this$1 = this;
101857
101858           if (this._ring !== null) { return null }
101859           var coord = new Array(this._pts.size()).fill(null);
101860           for (var i = 0; i < this._pts.size(); i++) {
101861             coord[i] = this$1._pts.get(i);
101862           }
101863           this._ring = this._geometryFactory.createLinearRing(coord);
101864           this._isHole = CGAlgorithms.isCCW(this._ring.getCoordinates());
101865         };
101866         EdgeRing.prototype.isIsolated = function isIsolated () {
101867           return this._label.getGeometryCount() === 1
101868         };
101869         EdgeRing.prototype.computePoints = function computePoints (start) {
101870             var this$1 = this;
101871
101872           this._startDe = start;
101873           var de = start;
101874           var isFirstEdge = true;
101875           do {
101876             if (de === null) { throw new TopologyException('Found null DirectedEdge') }
101877             if (de.getEdgeRing() === this$1) { throw new TopologyException('Directed Edge visited twice during ring-building at ' + de.getCoordinate()) }
101878             this$1._edges.add(de);
101879             var label = de.getLabel();
101880             Assert.isTrue(label.isArea());
101881             this$1.mergeLabel(label);
101882             this$1.addPoints(de.getEdge(), de.isForward(), isFirstEdge);
101883             isFirstEdge = false;
101884             this$1.setEdgeRing(de, this$1);
101885             de = this$1.getNext(de);
101886           } while (de !== this._startDe)
101887         };
101888         EdgeRing.prototype.getLinearRing = function getLinearRing () {
101889           return this._ring
101890         };
101891         EdgeRing.prototype.getCoordinate = function getCoordinate (i) {
101892           return this._pts.get(i)
101893         };
101894         EdgeRing.prototype.computeMaxNodeDegree = function computeMaxNodeDegree () {
101895             var this$1 = this;
101896
101897           this._maxNodeDegree = 0;
101898           var de = this._startDe;
101899           do {
101900             var node = de.getNode();
101901             var degree = node.getEdges().getOutgoingDegree(this$1);
101902             if (degree > this$1._maxNodeDegree) { this$1._maxNodeDegree = degree; }
101903             de = this$1.getNext(de);
101904           } while (de !== this._startDe)
101905           this._maxNodeDegree *= 2;
101906         };
101907         EdgeRing.prototype.addPoints = function addPoints (edge, isForward, isFirstEdge) {
101908             var this$1 = this;
101909
101910           var edgePts = edge.getCoordinates();
101911           if (isForward) {
101912             var startIndex = 1;
101913             if (isFirstEdge) { startIndex = 0; }
101914             for (var i = startIndex; i < edgePts.length; i++) {
101915               this$1._pts.add(edgePts[i]);
101916             }
101917           } else {
101918             var startIndex$1 = edgePts.length - 2;
101919             if (isFirstEdge) { startIndex$1 = edgePts.length - 1; }
101920             for (var i$1 = startIndex$1; i$1 >= 0; i$1--) {
101921               this$1._pts.add(edgePts[i$1]);
101922             }
101923           }
101924         };
101925         EdgeRing.prototype.isHole = function isHole () {
101926           return this._isHole
101927         };
101928         EdgeRing.prototype.setInResult = function setInResult () {
101929           var de = this._startDe;
101930           do {
101931             de.getEdge().setInResult(true);
101932             de = de.getNext();
101933           } while (de !== this._startDe)
101934         };
101935         EdgeRing.prototype.containsPoint = function containsPoint (p) {
101936           var shell = this.getLinearRing();
101937           var env = shell.getEnvelopeInternal();
101938           if (!env.contains(p)) { return false }
101939           if (!CGAlgorithms.isPointInRing(p, shell.getCoordinates())) { return false }
101940           for (var i = this._holes.iterator(); i.hasNext();) {
101941             var hole = i.next();
101942             if (hole.containsPoint(p)) { return false }
101943           }
101944           return true
101945         };
101946         EdgeRing.prototype.addHole = function addHole (ring) {
101947           this._holes.add(ring);
101948         };
101949         EdgeRing.prototype.isShell = function isShell () {
101950           return this._shell === null
101951         };
101952         EdgeRing.prototype.getLabel = function getLabel () {
101953           return this._label
101954         };
101955         EdgeRing.prototype.getEdges = function getEdges () {
101956           return this._edges
101957         };
101958         EdgeRing.prototype.getMaxNodeDegree = function getMaxNodeDegree () {
101959           if (this._maxNodeDegree < 0) { this.computeMaxNodeDegree(); }
101960           return this._maxNodeDegree
101961         };
101962         EdgeRing.prototype.getShell = function getShell () {
101963           return this._shell
101964         };
101965         EdgeRing.prototype.mergeLabel = function mergeLabel () {
101966           if (arguments.length === 1) {
101967             var deLabel = arguments[0];
101968             this.mergeLabel(deLabel, 0);
101969             this.mergeLabel(deLabel, 1);
101970           } else if (arguments.length === 2) {
101971             var deLabel$1 = arguments[0];
101972             var geomIndex = arguments[1];
101973             var loc = deLabel$1.getLocation(geomIndex, Position.RIGHT);
101974             if (loc === Location.NONE) { return null }
101975             if (this._label.getLocation(geomIndex) === Location.NONE) {
101976               this._label.setLocation(geomIndex, loc);
101977               return null
101978             }
101979           }
101980         };
101981         EdgeRing.prototype.setShell = function setShell (shell) {
101982           this._shell = shell;
101983           if (shell !== null) { shell.addHole(this); }
101984         };
101985         EdgeRing.prototype.toPolygon = function toPolygon (geometryFactory) {
101986             var this$1 = this;
101987
101988           var holeLR = new Array(this._holes.size()).fill(null);
101989           for (var i = 0; i < this._holes.size(); i++) {
101990             holeLR[i] = this$1._holes.get(i).getLinearRing();
101991           }
101992           var poly = geometryFactory.createPolygon(this.getLinearRing(), holeLR);
101993           return poly
101994         };
101995         EdgeRing.prototype.interfaces_ = function interfaces_ () {
101996           return []
101997         };
101998         EdgeRing.prototype.getClass = function getClass () {
101999           return EdgeRing
102000         };
102001
102002         var MinimalEdgeRing = (function (EdgeRing$$1) {
102003           function MinimalEdgeRing () {
102004             var start = arguments[0];
102005             var geometryFactory = arguments[1];
102006             EdgeRing$$1.call(this, start, geometryFactory);
102007           }
102008
102009           if ( EdgeRing$$1 ) { MinimalEdgeRing.__proto__ = EdgeRing$$1; }
102010           MinimalEdgeRing.prototype = Object.create( EdgeRing$$1 && EdgeRing$$1.prototype );
102011           MinimalEdgeRing.prototype.constructor = MinimalEdgeRing;
102012           MinimalEdgeRing.prototype.setEdgeRing = function setEdgeRing (de, er) {
102013             de.setMinEdgeRing(er);
102014           };
102015           MinimalEdgeRing.prototype.getNext = function getNext (de) {
102016             return de.getNextMin()
102017           };
102018           MinimalEdgeRing.prototype.interfaces_ = function interfaces_ () {
102019             return []
102020           };
102021           MinimalEdgeRing.prototype.getClass = function getClass () {
102022             return MinimalEdgeRing
102023           };
102024
102025           return MinimalEdgeRing;
102026         }(EdgeRing));
102027
102028         var MaximalEdgeRing = (function (EdgeRing$$1) {
102029           function MaximalEdgeRing () {
102030             var start = arguments[0];
102031             var geometryFactory = arguments[1];
102032             EdgeRing$$1.call(this, start, geometryFactory);
102033           }
102034
102035           if ( EdgeRing$$1 ) { MaximalEdgeRing.__proto__ = EdgeRing$$1; }
102036           MaximalEdgeRing.prototype = Object.create( EdgeRing$$1 && EdgeRing$$1.prototype );
102037           MaximalEdgeRing.prototype.constructor = MaximalEdgeRing;
102038           MaximalEdgeRing.prototype.buildMinimalRings = function buildMinimalRings () {
102039             var this$1 = this;
102040
102041             var minEdgeRings = new ArrayList();
102042             var de = this._startDe;
102043             do {
102044               if (de.getMinEdgeRing() === null) {
102045                 var minEr = new MinimalEdgeRing(de, this$1._geometryFactory);
102046                 minEdgeRings.add(minEr);
102047               }
102048               de = de.getNext();
102049             } while (de !== this._startDe)
102050             return minEdgeRings
102051           };
102052           MaximalEdgeRing.prototype.setEdgeRing = function setEdgeRing (de, er) {
102053             de.setEdgeRing(er);
102054           };
102055           MaximalEdgeRing.prototype.linkDirectedEdgesForMinimalEdgeRings = function linkDirectedEdgesForMinimalEdgeRings () {
102056             var this$1 = this;
102057
102058             var de = this._startDe;
102059             do {
102060               var node = de.getNode();
102061               node.getEdges().linkMinimalDirectedEdges(this$1);
102062               de = de.getNext();
102063             } while (de !== this._startDe)
102064           };
102065           MaximalEdgeRing.prototype.getNext = function getNext (de) {
102066             return de.getNext()
102067           };
102068           MaximalEdgeRing.prototype.interfaces_ = function interfaces_ () {
102069             return []
102070           };
102071           MaximalEdgeRing.prototype.getClass = function getClass () {
102072             return MaximalEdgeRing
102073           };
102074
102075           return MaximalEdgeRing;
102076         }(EdgeRing));
102077
102078         var GraphComponent = function GraphComponent () {
102079           this._label = null;
102080           this._isInResult = false;
102081           this._isCovered = false;
102082           this._isCoveredSet = false;
102083           this._isVisited = false;
102084           if (arguments.length === 0) ; else if (arguments.length === 1) {
102085             var label = arguments[0];
102086             this._label = label;
102087           }
102088         };
102089         GraphComponent.prototype.setVisited = function setVisited (isVisited) {
102090           this._isVisited = isVisited;
102091         };
102092         GraphComponent.prototype.setInResult = function setInResult (isInResult) {
102093           this._isInResult = isInResult;
102094         };
102095         GraphComponent.prototype.isCovered = function isCovered () {
102096           return this._isCovered
102097         };
102098         GraphComponent.prototype.isCoveredSet = function isCoveredSet () {
102099           return this._isCoveredSet
102100         };
102101         GraphComponent.prototype.setLabel = function setLabel (label) {
102102           this._label = label;
102103         };
102104         GraphComponent.prototype.getLabel = function getLabel () {
102105           return this._label
102106         };
102107         GraphComponent.prototype.setCovered = function setCovered (isCovered) {
102108           this._isCovered = isCovered;
102109           this._isCoveredSet = true;
102110         };
102111         GraphComponent.prototype.updateIM = function updateIM (im) {
102112           Assert.isTrue(this._label.getGeometryCount() >= 2, 'found partial label');
102113           this.computeIM(im);
102114         };
102115         GraphComponent.prototype.isInResult = function isInResult () {
102116           return this._isInResult
102117         };
102118         GraphComponent.prototype.isVisited = function isVisited () {
102119           return this._isVisited
102120         };
102121         GraphComponent.prototype.interfaces_ = function interfaces_ () {
102122           return []
102123         };
102124         GraphComponent.prototype.getClass = function getClass () {
102125           return GraphComponent
102126         };
102127
102128         var Node$1 = (function (GraphComponent$$1) {
102129           function Node () {
102130             GraphComponent$$1.call(this);
102131             this._coord = null;
102132             this._edges = null;
102133             var coord = arguments[0];
102134             var edges = arguments[1];
102135             this._coord = coord;
102136             this._edges = edges;
102137             this._label = new Label(0, Location.NONE);
102138           }
102139
102140           if ( GraphComponent$$1 ) { Node.__proto__ = GraphComponent$$1; }
102141           Node.prototype = Object.create( GraphComponent$$1 && GraphComponent$$1.prototype );
102142           Node.prototype.constructor = Node;
102143           Node.prototype.isIncidentEdgeInResult = function isIncidentEdgeInResult () {
102144             for (var it = this.getEdges().getEdges().iterator(); it.hasNext();) {
102145               var de = it.next();
102146               if (de.getEdge().isInResult()) { return true }
102147             }
102148             return false
102149           };
102150           Node.prototype.isIsolated = function isIsolated () {
102151             return this._label.getGeometryCount() === 1
102152           };
102153           Node.prototype.getCoordinate = function getCoordinate () {
102154             return this._coord
102155           };
102156           Node.prototype.print = function print (out) {
102157             out.println('node ' + this._coord + ' lbl: ' + this._label);
102158           };
102159           Node.prototype.computeIM = function computeIM (im) {};
102160           Node.prototype.computeMergedLocation = function computeMergedLocation (label2, eltIndex) {
102161             var loc = Location.NONE;
102162             loc = this._label.getLocation(eltIndex);
102163             if (!label2.isNull(eltIndex)) {
102164               var nLoc = label2.getLocation(eltIndex);
102165               if (loc !== Location.BOUNDARY) { loc = nLoc; }
102166             }
102167             return loc
102168           };
102169           Node.prototype.setLabel = function setLabel () {
102170             if (arguments.length === 2) {
102171               var argIndex = arguments[0];
102172               var onLocation = arguments[1];
102173               if (this._label === null) {
102174                 this._label = new Label(argIndex, onLocation);
102175               } else { this._label.setLocation(argIndex, onLocation); }
102176             } else { return GraphComponent$$1.prototype.setLabel.apply(this, arguments) }
102177           };
102178           Node.prototype.getEdges = function getEdges () {
102179             return this._edges
102180           };
102181           Node.prototype.mergeLabel = function mergeLabel () {
102182             var this$1 = this;
102183
102184             if (arguments[0] instanceof Node) {
102185               var n = arguments[0];
102186               this.mergeLabel(n._label);
102187             } else if (arguments[0] instanceof Label) {
102188               var label2 = arguments[0];
102189               for (var i = 0; i < 2; i++) {
102190                 var loc = this$1.computeMergedLocation(label2, i);
102191                 var thisLoc = this$1._label.getLocation(i);
102192                 if (thisLoc === Location.NONE) { this$1._label.setLocation(i, loc); }
102193               }
102194             }
102195           };
102196           Node.prototype.add = function add (e) {
102197             this._edges.insert(e);
102198             e.setNode(this);
102199           };
102200           Node.prototype.setLabelBoundary = function setLabelBoundary (argIndex) {
102201             if (this._label === null) { return null }
102202             var loc = Location.NONE;
102203             if (this._label !== null) { loc = this._label.getLocation(argIndex); }
102204             var newLoc = null;
102205             switch (loc) {
102206               case Location.BOUNDARY:
102207                 newLoc = Location.INTERIOR;
102208                 break
102209               case Location.INTERIOR:
102210                 newLoc = Location.BOUNDARY;
102211                 break
102212               default:
102213                 newLoc = Location.BOUNDARY;
102214                 break
102215             }
102216             this._label.setLocation(argIndex, newLoc);
102217           };
102218           Node.prototype.interfaces_ = function interfaces_ () {
102219             return []
102220           };
102221           Node.prototype.getClass = function getClass () {
102222             return Node
102223           };
102224
102225           return Node;
102226         }(GraphComponent));
102227
102228         var NodeMap = function NodeMap () {
102229           this.nodeMap = new TreeMap();
102230           this.nodeFact = null;
102231           var nodeFact = arguments[0];
102232           this.nodeFact = nodeFact;
102233         };
102234         NodeMap.prototype.find = function find (coord) {
102235           return this.nodeMap.get(coord)
102236         };
102237         NodeMap.prototype.addNode = function addNode () {
102238           if (arguments[0] instanceof Coordinate) {
102239             var coord = arguments[0];
102240             var node = this.nodeMap.get(coord);
102241             if (node === null) {
102242               node = this.nodeFact.createNode(coord);
102243               this.nodeMap.put(coord, node);
102244             }
102245             return node
102246           } else if (arguments[0] instanceof Node$1) {
102247             var n = arguments[0];
102248             var node$1 = this.nodeMap.get(n.getCoordinate());
102249             if (node$1 === null) {
102250               this.nodeMap.put(n.getCoordinate(), n);
102251               return n
102252             }
102253             node$1.mergeLabel(n);
102254             return node$1
102255           }
102256         };
102257         NodeMap.prototype.print = function print (out) {
102258           for (var it = this.iterator(); it.hasNext();) {
102259             var n = it.next();
102260             n.print(out);
102261           }
102262         };
102263         NodeMap.prototype.iterator = function iterator () {
102264           return this.nodeMap.values().iterator()
102265         };
102266         NodeMap.prototype.values = function values () {
102267           return this.nodeMap.values()
102268         };
102269         NodeMap.prototype.getBoundaryNodes = function getBoundaryNodes (geomIndex) {
102270           var bdyNodes = new ArrayList();
102271           for (var i = this.iterator(); i.hasNext();) {
102272             var node = i.next();
102273             if (node.getLabel().getLocation(geomIndex) === Location.BOUNDARY) { bdyNodes.add(node); }
102274           }
102275           return bdyNodes
102276         };
102277         NodeMap.prototype.add = function add (e) {
102278           var p = e.getCoordinate();
102279           var n = this.addNode(p);
102280           n.add(e);
102281         };
102282         NodeMap.prototype.interfaces_ = function interfaces_ () {
102283           return []
102284         };
102285         NodeMap.prototype.getClass = function getClass () {
102286           return NodeMap
102287         };
102288
102289         var Quadrant = function Quadrant () {};
102290
102291         var staticAccessors$21 = { NE: { configurable: true },NW: { configurable: true },SW: { configurable: true },SE: { configurable: true } };
102292
102293         Quadrant.prototype.interfaces_ = function interfaces_ () {
102294           return []
102295         };
102296         Quadrant.prototype.getClass = function getClass () {
102297           return Quadrant
102298         };
102299         Quadrant.isNorthern = function isNorthern (quad) {
102300           return quad === Quadrant.NE || quad === Quadrant.NW
102301         };
102302         Quadrant.isOpposite = function isOpposite (quad1, quad2) {
102303           if (quad1 === quad2) { return false }
102304           var diff = (quad1 - quad2 + 4) % 4;
102305           if (diff === 2) { return true }
102306           return false
102307         };
102308         Quadrant.commonHalfPlane = function commonHalfPlane (quad1, quad2) {
102309           if (quad1 === quad2) { return quad1 }
102310           var diff = (quad1 - quad2 + 4) % 4;
102311           if (diff === 2) { return -1 }
102312           var min = quad1 < quad2 ? quad1 : quad2;
102313           var max = quad1 > quad2 ? quad1 : quad2;
102314           if (min === 0 && max === 3) { return 3 }
102315           return min
102316         };
102317         Quadrant.isInHalfPlane = function isInHalfPlane (quad, halfPlane) {
102318           if (halfPlane === Quadrant.SE) {
102319             return quad === Quadrant.SE || quad === Quadrant.SW
102320           }
102321           return quad === halfPlane || quad === halfPlane + 1
102322         };
102323         Quadrant.quadrant = function quadrant () {
102324           if (typeof arguments[0] === 'number' && typeof arguments[1] === 'number') {
102325             var dx = arguments[0];
102326             var dy = arguments[1];
102327             if (dx === 0.0 && dy === 0.0) { throw new IllegalArgumentException('Cannot compute the quadrant for point ( ' + dx + ', ' + dy + ' )') }
102328             if (dx >= 0.0) {
102329               if (dy >= 0.0) { return Quadrant.NE; } else { return Quadrant.SE }
102330             } else {
102331               if (dy >= 0.0) { return Quadrant.NW; } else { return Quadrant.SW }
102332             }
102333           } else if (arguments[0] instanceof Coordinate && arguments[1] instanceof Coordinate) {
102334             var p0 = arguments[0];
102335             var p1 = arguments[1];
102336             if (p1.x === p0.x && p1.y === p0.y) { throw new IllegalArgumentException('Cannot compute the quadrant for two identical points ' + p0) }
102337             if (p1.x >= p0.x) {
102338               if (p1.y >= p0.y) { return Quadrant.NE; } else { return Quadrant.SE }
102339             } else {
102340               if (p1.y >= p0.y) { return Quadrant.NW; } else { return Quadrant.SW }
102341             }
102342           }
102343         };
102344         staticAccessors$21.NE.get = function () { return 0 };
102345         staticAccessors$21.NW.get = function () { return 1 };
102346         staticAccessors$21.SW.get = function () { return 2 };
102347         staticAccessors$21.SE.get = function () { return 3 };
102348
102349         Object.defineProperties( Quadrant, staticAccessors$21 );
102350
102351         var EdgeEnd = function EdgeEnd () {
102352           this._edge = null;
102353           this._label = null;
102354           this._node = null;
102355           this._p0 = null;
102356           this._p1 = null;
102357           this._dx = null;
102358           this._dy = null;
102359           this._quadrant = null;
102360           if (arguments.length === 1) {
102361             var edge = arguments[0];
102362             this._edge = edge;
102363           } else if (arguments.length === 3) {
102364             var edge$1 = arguments[0];
102365             var p0 = arguments[1];
102366             var p1 = arguments[2];
102367             var label = null;
102368             this._edge = edge$1;
102369             this.init(p0, p1);
102370             this._label = label;
102371           } else if (arguments.length === 4) {
102372             var edge$2 = arguments[0];
102373             var p0$1 = arguments[1];
102374             var p1$1 = arguments[2];
102375             var label$1 = arguments[3];
102376             this._edge = edge$2;
102377             this.init(p0$1, p1$1);
102378             this._label = label$1;
102379           }
102380         };
102381         EdgeEnd.prototype.compareDirection = function compareDirection (e) {
102382           if (this._dx === e._dx && this._dy === e._dy) { return 0 }
102383           if (this._quadrant > e._quadrant) { return 1 }
102384           if (this._quadrant < e._quadrant) { return -1 }
102385           return CGAlgorithms.computeOrientation(e._p0, e._p1, this._p1)
102386         };
102387         EdgeEnd.prototype.getDy = function getDy () {
102388           return this._dy
102389         };
102390         EdgeEnd.prototype.getCoordinate = function getCoordinate () {
102391           return this._p0
102392         };
102393         EdgeEnd.prototype.setNode = function setNode (node) {
102394           this._node = node;
102395         };
102396         EdgeEnd.prototype.print = function print (out) {
102397           var angle = Math.atan2(this._dy, this._dx);
102398           var className = this.getClass().getName();
102399           var lastDotPos = className.lastIndexOf('.');
102400           var name = className.substring(lastDotPos + 1);
102401           out.print('  ' + name + ': ' + this._p0 + ' - ' + this._p1 + ' ' + this._quadrant + ':' + angle + '   ' + this._label);
102402         };
102403         EdgeEnd.prototype.compareTo = function compareTo (obj) {
102404           var e = obj;
102405           return this.compareDirection(e)
102406         };
102407         EdgeEnd.prototype.getDirectedCoordinate = function getDirectedCoordinate () {
102408           return this._p1
102409         };
102410         EdgeEnd.prototype.getDx = function getDx () {
102411           return this._dx
102412         };
102413         EdgeEnd.prototype.getLabel = function getLabel () {
102414           return this._label
102415         };
102416         EdgeEnd.prototype.getEdge = function getEdge () {
102417           return this._edge
102418         };
102419         EdgeEnd.prototype.getQuadrant = function getQuadrant () {
102420           return this._quadrant
102421         };
102422         EdgeEnd.prototype.getNode = function getNode () {
102423           return this._node
102424         };
102425         EdgeEnd.prototype.toString = function toString () {
102426           var angle = Math.atan2(this._dy, this._dx);
102427           var className = this.getClass().getName();
102428           var lastDotPos = className.lastIndexOf('.');
102429           var name = className.substring(lastDotPos + 1);
102430           return '  ' + name + ': ' + this._p0 + ' - ' + this._p1 + ' ' + this._quadrant + ':' + angle + '   ' + this._label
102431         };
102432         EdgeEnd.prototype.computeLabel = function computeLabel (boundaryNodeRule) {};
102433         EdgeEnd.prototype.init = function init (p0, p1) {
102434           this._p0 = p0;
102435           this._p1 = p1;
102436           this._dx = p1.x - p0.x;
102437           this._dy = p1.y - p0.y;
102438           this._quadrant = Quadrant.quadrant(this._dx, this._dy);
102439           Assert.isTrue(!(this._dx === 0 && this._dy === 0), 'EdgeEnd with identical endpoints found');
102440         };
102441         EdgeEnd.prototype.interfaces_ = function interfaces_ () {
102442           return [Comparable]
102443         };
102444         EdgeEnd.prototype.getClass = function getClass () {
102445           return EdgeEnd
102446         };
102447
102448         var DirectedEdge = (function (EdgeEnd$$1) {
102449           function DirectedEdge () {
102450             var edge = arguments[0];
102451             var isForward = arguments[1];
102452             EdgeEnd$$1.call(this, edge);
102453             this._isForward = null;
102454             this._isInResult = false;
102455             this._isVisited = false;
102456             this._sym = null;
102457             this._next = null;
102458             this._nextMin = null;
102459             this._edgeRing = null;
102460             this._minEdgeRing = null;
102461             this._depth = [0, -999, -999];
102462             this._isForward = isForward;
102463             if (isForward) {
102464               this.init(edge.getCoordinate(0), edge.getCoordinate(1));
102465             } else {
102466               var n = edge.getNumPoints() - 1;
102467               this.init(edge.getCoordinate(n), edge.getCoordinate(n - 1));
102468             }
102469             this.computeDirectedLabel();
102470           }
102471
102472           if ( EdgeEnd$$1 ) { DirectedEdge.__proto__ = EdgeEnd$$1; }
102473           DirectedEdge.prototype = Object.create( EdgeEnd$$1 && EdgeEnd$$1.prototype );
102474           DirectedEdge.prototype.constructor = DirectedEdge;
102475           DirectedEdge.prototype.getNextMin = function getNextMin () {
102476             return this._nextMin
102477           };
102478           DirectedEdge.prototype.getDepth = function getDepth (position) {
102479             return this._depth[position]
102480           };
102481           DirectedEdge.prototype.setVisited = function setVisited (isVisited) {
102482             this._isVisited = isVisited;
102483           };
102484           DirectedEdge.prototype.computeDirectedLabel = function computeDirectedLabel () {
102485             this._label = new Label(this._edge.getLabel());
102486             if (!this._isForward) { this._label.flip(); }
102487           };
102488           DirectedEdge.prototype.getNext = function getNext () {
102489             return this._next
102490           };
102491           DirectedEdge.prototype.setDepth = function setDepth (position, depthVal) {
102492             if (this._depth[position] !== -999) {
102493               if (this._depth[position] !== depthVal) { throw new TopologyException('assigned depths do not match', this.getCoordinate()) }
102494             }
102495             this._depth[position] = depthVal;
102496           };
102497           DirectedEdge.prototype.isInteriorAreaEdge = function isInteriorAreaEdge () {
102498             var this$1 = this;
102499
102500             var isInteriorAreaEdge = true;
102501             for (var i = 0; i < 2; i++) {
102502               if (!(this$1._label.isArea(i) && this$1._label.getLocation(i, Position.LEFT) === Location.INTERIOR && this$1._label.getLocation(i, Position.RIGHT) === Location.INTERIOR)) {
102503                 isInteriorAreaEdge = false;
102504               }
102505             }
102506             return isInteriorAreaEdge
102507           };
102508           DirectedEdge.prototype.setNextMin = function setNextMin (nextMin) {
102509             this._nextMin = nextMin;
102510           };
102511           DirectedEdge.prototype.print = function print (out) {
102512             EdgeEnd$$1.prototype.print.call(this, out);
102513             out.print(' ' + this._depth[Position.LEFT] + '/' + this._depth[Position.RIGHT]);
102514             out.print(' (' + this.getDepthDelta() + ')');
102515             if (this._isInResult) { out.print(' inResult'); }
102516           };
102517           DirectedEdge.prototype.setMinEdgeRing = function setMinEdgeRing (minEdgeRing) {
102518             this._minEdgeRing = minEdgeRing;
102519           };
102520           DirectedEdge.prototype.isLineEdge = function isLineEdge () {
102521             var isLine = this._label.isLine(0) || this._label.isLine(1);
102522             var isExteriorIfArea0 = !this._label.isArea(0) || this._label.allPositionsEqual(0, Location.EXTERIOR);
102523             var isExteriorIfArea1 = !this._label.isArea(1) || this._label.allPositionsEqual(1, Location.EXTERIOR);
102524             return isLine && isExteriorIfArea0 && isExteriorIfArea1
102525           };
102526           DirectedEdge.prototype.setEdgeRing = function setEdgeRing (edgeRing) {
102527             this._edgeRing = edgeRing;
102528           };
102529           DirectedEdge.prototype.getMinEdgeRing = function getMinEdgeRing () {
102530             return this._minEdgeRing
102531           };
102532           DirectedEdge.prototype.getDepthDelta = function getDepthDelta () {
102533             var depthDelta = this._edge.getDepthDelta();
102534             if (!this._isForward) { depthDelta = -depthDelta; }
102535             return depthDelta
102536           };
102537           DirectedEdge.prototype.setInResult = function setInResult (isInResult) {
102538             this._isInResult = isInResult;
102539           };
102540           DirectedEdge.prototype.getSym = function getSym () {
102541             return this._sym
102542           };
102543           DirectedEdge.prototype.isForward = function isForward () {
102544             return this._isForward
102545           };
102546           DirectedEdge.prototype.getEdge = function getEdge () {
102547             return this._edge
102548           };
102549           DirectedEdge.prototype.printEdge = function printEdge (out) {
102550             this.print(out);
102551             out.print(' ');
102552             if (this._isForward) { this._edge.print(out); } else { this._edge.printReverse(out); }
102553           };
102554           DirectedEdge.prototype.setSym = function setSym (de) {
102555             this._sym = de;
102556           };
102557           DirectedEdge.prototype.setVisitedEdge = function setVisitedEdge (isVisited) {
102558             this.setVisited(isVisited);
102559             this._sym.setVisited(isVisited);
102560           };
102561           DirectedEdge.prototype.setEdgeDepths = function setEdgeDepths (position, depth) {
102562             var depthDelta = this.getEdge().getDepthDelta();
102563             if (!this._isForward) { depthDelta = -depthDelta; }
102564             var directionFactor = 1;
102565             if (position === Position.LEFT) { directionFactor = -1; }
102566             var oppositePos = Position.opposite(position);
102567             var delta = depthDelta * directionFactor;
102568             var oppositeDepth = depth + delta;
102569             this.setDepth(position, depth);
102570             this.setDepth(oppositePos, oppositeDepth);
102571           };
102572           DirectedEdge.prototype.getEdgeRing = function getEdgeRing () {
102573             return this._edgeRing
102574           };
102575           DirectedEdge.prototype.isInResult = function isInResult () {
102576             return this._isInResult
102577           };
102578           DirectedEdge.prototype.setNext = function setNext (next) {
102579             this._next = next;
102580           };
102581           DirectedEdge.prototype.isVisited = function isVisited () {
102582             return this._isVisited
102583           };
102584           DirectedEdge.prototype.interfaces_ = function interfaces_ () {
102585             return []
102586           };
102587           DirectedEdge.prototype.getClass = function getClass () {
102588             return DirectedEdge
102589           };
102590           DirectedEdge.depthFactor = function depthFactor (currLocation, nextLocation) {
102591             if (currLocation === Location.EXTERIOR && nextLocation === Location.INTERIOR) { return 1; } else if (currLocation === Location.INTERIOR && nextLocation === Location.EXTERIOR) { return -1 }
102592             return 0
102593           };
102594
102595           return DirectedEdge;
102596         }(EdgeEnd));
102597
102598         var NodeFactory = function NodeFactory () {};
102599
102600         NodeFactory.prototype.createNode = function createNode (coord) {
102601           return new Node$1(coord, null)
102602         };
102603         NodeFactory.prototype.interfaces_ = function interfaces_ () {
102604           return []
102605         };
102606         NodeFactory.prototype.getClass = function getClass () {
102607           return NodeFactory
102608         };
102609
102610         var PlanarGraph = function PlanarGraph () {
102611           this._edges = new ArrayList();
102612           this._nodes = null;
102613           this._edgeEndList = new ArrayList();
102614           if (arguments.length === 0) {
102615             this._nodes = new NodeMap(new NodeFactory());
102616           } else if (arguments.length === 1) {
102617             var nodeFact = arguments[0];
102618             this._nodes = new NodeMap(nodeFact);
102619           }
102620         };
102621         PlanarGraph.prototype.printEdges = function printEdges (out) {
102622             var this$1 = this;
102623
102624           out.println('Edges:');
102625           for (var i = 0; i < this._edges.size(); i++) {
102626             out.println('edge ' + i + ':');
102627             var e = this$1._edges.get(i);
102628             e.print(out);
102629             e.eiList.print(out);
102630           }
102631         };
102632         PlanarGraph.prototype.find = function find (coord) {
102633           return this._nodes.find(coord)
102634         };
102635         PlanarGraph.prototype.addNode = function addNode () {
102636           if (arguments[0] instanceof Node$1) {
102637             var node = arguments[0];
102638             return this._nodes.addNode(node)
102639           } else if (arguments[0] instanceof Coordinate) {
102640             var coord = arguments[0];
102641             return this._nodes.addNode(coord)
102642           }
102643         };
102644         PlanarGraph.prototype.getNodeIterator = function getNodeIterator () {
102645           return this._nodes.iterator()
102646         };
102647         PlanarGraph.prototype.linkResultDirectedEdges = function linkResultDirectedEdges () {
102648           for (var nodeit = this._nodes.iterator(); nodeit.hasNext();) {
102649             var node = nodeit.next();
102650             node.getEdges().linkResultDirectedEdges();
102651           }
102652         };
102653         PlanarGraph.prototype.debugPrintln = function debugPrintln (o) {
102654           System.out.println(o);
102655         };
102656         PlanarGraph.prototype.isBoundaryNode = function isBoundaryNode (geomIndex, coord) {
102657           var node = this._nodes.find(coord);
102658           if (node === null) { return false }
102659           var label = node.getLabel();
102660           if (label !== null && label.getLocation(geomIndex) === Location.BOUNDARY) { return true }
102661           return false
102662         };
102663         PlanarGraph.prototype.linkAllDirectedEdges = function linkAllDirectedEdges () {
102664           for (var nodeit = this._nodes.iterator(); nodeit.hasNext();) {
102665             var node = nodeit.next();
102666             node.getEdges().linkAllDirectedEdges();
102667           }
102668         };
102669         PlanarGraph.prototype.matchInSameDirection = function matchInSameDirection (p0, p1, ep0, ep1) {
102670           if (!p0.equals(ep0)) { return false }
102671           if (CGAlgorithms.computeOrientation(p0, p1, ep1) === CGAlgorithms.COLLINEAR && Quadrant.quadrant(p0, p1) === Quadrant.quadrant(ep0, ep1)) { return true }
102672           return false
102673         };
102674         PlanarGraph.prototype.getEdgeEnds = function getEdgeEnds () {
102675           return this._edgeEndList
102676         };
102677         PlanarGraph.prototype.debugPrint = function debugPrint (o) {
102678           System.out.print(o);
102679         };
102680         PlanarGraph.prototype.getEdgeIterator = function getEdgeIterator () {
102681           return this._edges.iterator()
102682         };
102683         PlanarGraph.prototype.findEdgeInSameDirection = function findEdgeInSameDirection (p0, p1) {
102684             var this$1 = this;
102685
102686           for (var i = 0; i < this._edges.size(); i++) {
102687             var e = this$1._edges.get(i);
102688             var eCoord = e.getCoordinates();
102689             if (this$1.matchInSameDirection(p0, p1, eCoord[0], eCoord[1])) { return e }
102690             if (this$1.matchInSameDirection(p0, p1, eCoord[eCoord.length - 1], eCoord[eCoord.length - 2])) { return e }
102691           }
102692           return null
102693         };
102694         PlanarGraph.prototype.insertEdge = function insertEdge (e) {
102695           this._edges.add(e);
102696         };
102697         PlanarGraph.prototype.findEdgeEnd = function findEdgeEnd (e) {
102698           for (var i = this.getEdgeEnds().iterator(); i.hasNext();) {
102699             var ee = i.next();
102700             if (ee.getEdge() === e) { return ee }
102701           }
102702           return null
102703         };
102704         PlanarGraph.prototype.addEdges = function addEdges (edgesToAdd) {
102705             var this$1 = this;
102706
102707           for (var it = edgesToAdd.iterator(); it.hasNext();) {
102708             var e = it.next();
102709             this$1._edges.add(e);
102710             var de1 = new DirectedEdge(e, true);
102711             var de2 = new DirectedEdge(e, false);
102712             de1.setSym(de2);
102713             de2.setSym(de1);
102714             this$1.add(de1);
102715             this$1.add(de2);
102716           }
102717         };
102718         PlanarGraph.prototype.add = function add (e) {
102719           this._nodes.add(e);
102720           this._edgeEndList.add(e);
102721         };
102722         PlanarGraph.prototype.getNodes = function getNodes () {
102723           return this._nodes.values()
102724         };
102725         PlanarGraph.prototype.findEdge = function findEdge (p0, p1) {
102726             var this$1 = this;
102727
102728           for (var i = 0; i < this._edges.size(); i++) {
102729             var e = this$1._edges.get(i);
102730             var eCoord = e.getCoordinates();
102731             if (p0.equals(eCoord[0]) && p1.equals(eCoord[1])) { return e }
102732           }
102733           return null
102734         };
102735         PlanarGraph.prototype.interfaces_ = function interfaces_ () {
102736           return []
102737         };
102738         PlanarGraph.prototype.getClass = function getClass () {
102739           return PlanarGraph
102740         };
102741         PlanarGraph.linkResultDirectedEdges = function linkResultDirectedEdges (nodes) {
102742           for (var nodeit = nodes.iterator(); nodeit.hasNext();) {
102743             var node = nodeit.next();
102744             node.getEdges().linkResultDirectedEdges();
102745           }
102746         };
102747
102748         var PolygonBuilder = function PolygonBuilder () {
102749           this._geometryFactory = null;
102750           this._shellList = new ArrayList();
102751           var geometryFactory = arguments[0];
102752           this._geometryFactory = geometryFactory;
102753         };
102754         PolygonBuilder.prototype.sortShellsAndHoles = function sortShellsAndHoles (edgeRings, shellList, freeHoleList) {
102755           for (var it = edgeRings.iterator(); it.hasNext();) {
102756             var er = it.next();
102757             if (er.isHole()) {
102758               freeHoleList.add(er);
102759             } else {
102760               shellList.add(er);
102761             }
102762           }
102763         };
102764         PolygonBuilder.prototype.computePolygons = function computePolygons (shellList) {
102765             var this$1 = this;
102766
102767           var resultPolyList = new ArrayList();
102768           for (var it = shellList.iterator(); it.hasNext();) {
102769             var er = it.next();
102770             var poly = er.toPolygon(this$1._geometryFactory);
102771             resultPolyList.add(poly);
102772           }
102773           return resultPolyList
102774         };
102775         PolygonBuilder.prototype.placeFreeHoles = function placeFreeHoles (shellList, freeHoleList) {
102776             var this$1 = this;
102777
102778           for (var it = freeHoleList.iterator(); it.hasNext();) {
102779             var hole = it.next();
102780             if (hole.getShell() === null) {
102781               var shell = this$1.findEdgeRingContaining(hole, shellList);
102782               if (shell === null) { throw new TopologyException('unable to assign hole to a shell', hole.getCoordinate(0)) }
102783               hole.setShell(shell);
102784             }
102785           }
102786         };
102787         PolygonBuilder.prototype.buildMinimalEdgeRings = function buildMinimalEdgeRings (maxEdgeRings, shellList, freeHoleList) {
102788             var this$1 = this;
102789
102790           var edgeRings = new ArrayList();
102791           for (var it = maxEdgeRings.iterator(); it.hasNext();) {
102792             var er = it.next();
102793             if (er.getMaxNodeDegree() > 2) {
102794               er.linkDirectedEdgesForMinimalEdgeRings();
102795               var minEdgeRings = er.buildMinimalRings();
102796               var shell = this$1.findShell(minEdgeRings);
102797               if (shell !== null) {
102798                 this$1.placePolygonHoles(shell, minEdgeRings);
102799                 shellList.add(shell);
102800               } else {
102801                 freeHoleList.addAll(minEdgeRings);
102802               }
102803             } else {
102804               edgeRings.add(er);
102805             }
102806           }
102807           return edgeRings
102808         };
102809         PolygonBuilder.prototype.containsPoint = function containsPoint (p) {
102810           for (var it = this._shellList.iterator(); it.hasNext();) {
102811             var er = it.next();
102812             if (er.containsPoint(p)) { return true }
102813           }
102814           return false
102815         };
102816         PolygonBuilder.prototype.buildMaximalEdgeRings = function buildMaximalEdgeRings (dirEdges) {
102817             var this$1 = this;
102818
102819           var maxEdgeRings = new ArrayList();
102820           for (var it = dirEdges.iterator(); it.hasNext();) {
102821             var de = it.next();
102822             if (de.isInResult() && de.getLabel().isArea()) {
102823               if (de.getEdgeRing() === null) {
102824                 var er = new MaximalEdgeRing(de, this$1._geometryFactory);
102825                 maxEdgeRings.add(er);
102826                 er.setInResult();
102827               }
102828             }
102829           }
102830           return maxEdgeRings
102831         };
102832         PolygonBuilder.prototype.placePolygonHoles = function placePolygonHoles (shell, minEdgeRings) {
102833           for (var it = minEdgeRings.iterator(); it.hasNext();) {
102834             var er = it.next();
102835             if (er.isHole()) {
102836               er.setShell(shell);
102837             }
102838           }
102839         };
102840         PolygonBuilder.prototype.getPolygons = function getPolygons () {
102841           var resultPolyList = this.computePolygons(this._shellList);
102842           return resultPolyList
102843         };
102844         PolygonBuilder.prototype.findEdgeRingContaining = function findEdgeRingContaining (testEr, shellList) {
102845           var testRing = testEr.getLinearRing();
102846           var testEnv = testRing.getEnvelopeInternal();
102847           var testPt = testRing.getCoordinateN(0);
102848           var minShell = null;
102849           var minEnv = null;
102850           for (var it = shellList.iterator(); it.hasNext();) {
102851             var tryShell = it.next();
102852             var tryRing = tryShell.getLinearRing();
102853             var tryEnv = tryRing.getEnvelopeInternal();
102854             if (minShell !== null) { minEnv = minShell.getLinearRing().getEnvelopeInternal(); }
102855             var isContained = false;
102856             if (tryEnv.contains(testEnv) && CGAlgorithms.isPointInRing(testPt, tryRing.getCoordinates())) { isContained = true; }
102857             if (isContained) {
102858               if (minShell === null || minEnv.contains(tryEnv)) {
102859                 minShell = tryShell;
102860               }
102861             }
102862           }
102863           return minShell
102864         };
102865         PolygonBuilder.prototype.findShell = function findShell (minEdgeRings) {
102866           var shellCount = 0;
102867           var shell = null;
102868           for (var it = minEdgeRings.iterator(); it.hasNext();) {
102869             var er = it.next();
102870             if (!er.isHole()) {
102871               shell = er;
102872               shellCount++;
102873             }
102874           }
102875           Assert.isTrue(shellCount <= 1, 'found two shells in MinimalEdgeRing list');
102876           return shell
102877         };
102878         PolygonBuilder.prototype.add = function add () {
102879           if (arguments.length === 1) {
102880             var graph = arguments[0];
102881             this.add(graph.getEdgeEnds(), graph.getNodes());
102882           } else if (arguments.length === 2) {
102883             var dirEdges = arguments[0];
102884             var nodes = arguments[1];
102885             PlanarGraph.linkResultDirectedEdges(nodes);
102886             var maxEdgeRings = this.buildMaximalEdgeRings(dirEdges);
102887             var freeHoleList = new ArrayList();
102888             var edgeRings = this.buildMinimalEdgeRings(maxEdgeRings, this._shellList, freeHoleList);
102889             this.sortShellsAndHoles(edgeRings, this._shellList, freeHoleList);
102890             this.placeFreeHoles(this._shellList, freeHoleList);
102891           }
102892         };
102893         PolygonBuilder.prototype.interfaces_ = function interfaces_ () {
102894           return []
102895         };
102896         PolygonBuilder.prototype.getClass = function getClass () {
102897           return PolygonBuilder
102898         };
102899
102900         var Boundable = function Boundable () {};
102901
102902         Boundable.prototype.getBounds = function getBounds () {};
102903         Boundable.prototype.interfaces_ = function interfaces_ () {
102904           return []
102905         };
102906         Boundable.prototype.getClass = function getClass () {
102907           return Boundable
102908         };
102909
102910         var ItemBoundable = function ItemBoundable () {
102911           this._bounds = null;
102912           this._item = null;
102913           var bounds = arguments[0];
102914           var item = arguments[1];
102915           this._bounds = bounds;
102916           this._item = item;
102917         };
102918         ItemBoundable.prototype.getItem = function getItem () {
102919           return this._item
102920         };
102921         ItemBoundable.prototype.getBounds = function getBounds () {
102922           return this._bounds
102923         };
102924         ItemBoundable.prototype.interfaces_ = function interfaces_ () {
102925           return [Boundable, Serializable]
102926         };
102927         ItemBoundable.prototype.getClass = function getClass () {
102928           return ItemBoundable
102929         };
102930
102931         var PriorityQueue = function PriorityQueue () {
102932           this._size = null;
102933           this._items = null;
102934           this._size = 0;
102935           this._items = new ArrayList();
102936           this._items.add(null);
102937         };
102938         PriorityQueue.prototype.poll = function poll () {
102939           if (this.isEmpty()) { return null }
102940           var minItem = this._items.get(1);
102941           this._items.set(1, this._items.get(this._size));
102942           this._size -= 1;
102943           this.reorder(1);
102944           return minItem
102945         };
102946         PriorityQueue.prototype.size = function size () {
102947           return this._size
102948         };
102949         PriorityQueue.prototype.reorder = function reorder (hole) {
102950             var this$1 = this;
102951
102952           var child = null;
102953           var tmp = this._items.get(hole);
102954           for (; hole * 2 <= this._size; hole = child) {
102955             child = hole * 2;
102956             if (child !== this$1._size && this$1._items.get(child + 1).compareTo(this$1._items.get(child)) < 0) { child++; }
102957             if (this$1._items.get(child).compareTo(tmp) < 0) { this$1._items.set(hole, this$1._items.get(child)); } else { break }
102958           }
102959           this._items.set(hole, tmp);
102960         };
102961         PriorityQueue.prototype.clear = function clear () {
102962           this._size = 0;
102963           this._items.clear();
102964         };
102965         PriorityQueue.prototype.isEmpty = function isEmpty () {
102966           return this._size === 0
102967         };
102968         PriorityQueue.prototype.add = function add (x) {
102969             var this$1 = this;
102970
102971           this._items.add(null);
102972           this._size += 1;
102973           var hole = this._size;
102974           this._items.set(0, x);
102975           for (; x.compareTo(this._items.get(Math.trunc(hole / 2))) < 0; hole /= 2) {
102976             this$1._items.set(hole, this$1._items.get(Math.trunc(hole / 2)));
102977           }
102978           this._items.set(hole, x);
102979         };
102980         PriorityQueue.prototype.interfaces_ = function interfaces_ () {
102981           return []
102982         };
102983         PriorityQueue.prototype.getClass = function getClass () {
102984           return PriorityQueue
102985         };
102986
102987         var ItemVisitor = function ItemVisitor () {};
102988
102989         ItemVisitor.prototype.visitItem = function visitItem (item) {};
102990         ItemVisitor.prototype.interfaces_ = function interfaces_ () {
102991           return []
102992         };
102993         ItemVisitor.prototype.getClass = function getClass () {
102994           return ItemVisitor
102995         };
102996
102997         var SpatialIndex = function SpatialIndex () {};
102998
102999         SpatialIndex.prototype.insert = function insert (itemEnv, item) {};
103000         SpatialIndex.prototype.remove = function remove (itemEnv, item) {};
103001         SpatialIndex.prototype.query = function query () {
103002           // if (arguments.length === 1) {
103003           // const searchEnv = arguments[0]
103004           // } else if (arguments.length === 2) {
103005           // const searchEnv = arguments[0]
103006           // const visitor = arguments[1]
103007           // }
103008         };
103009         SpatialIndex.prototype.interfaces_ = function interfaces_ () {
103010           return []
103011         };
103012         SpatialIndex.prototype.getClass = function getClass () {
103013           return SpatialIndex
103014         };
103015
103016         var AbstractNode = function AbstractNode () {
103017           this._childBoundables = new ArrayList();
103018           this._bounds = null;
103019           this._level = null;
103020           if (arguments.length === 0) ; else if (arguments.length === 1) {
103021             var level = arguments[0];
103022             this._level = level;
103023           }
103024         };
103025
103026         var staticAccessors$22 = { serialVersionUID: { configurable: true } };
103027         AbstractNode.prototype.getLevel = function getLevel () {
103028           return this._level
103029         };
103030         AbstractNode.prototype.size = function size () {
103031           return this._childBoundables.size()
103032         };
103033         AbstractNode.prototype.getChildBoundables = function getChildBoundables () {
103034           return this._childBoundables
103035         };
103036         AbstractNode.prototype.addChildBoundable = function addChildBoundable (childBoundable) {
103037           Assert.isTrue(this._bounds === null);
103038           this._childBoundables.add(childBoundable);
103039         };
103040         AbstractNode.prototype.isEmpty = function isEmpty () {
103041           return this._childBoundables.isEmpty()
103042         };
103043         AbstractNode.prototype.getBounds = function getBounds () {
103044           if (this._bounds === null) {
103045             this._bounds = this.computeBounds();
103046           }
103047           return this._bounds
103048         };
103049         AbstractNode.prototype.interfaces_ = function interfaces_ () {
103050           return [Boundable, Serializable]
103051         };
103052         AbstractNode.prototype.getClass = function getClass () {
103053           return AbstractNode
103054         };
103055         staticAccessors$22.serialVersionUID.get = function () { return 6493722185909573708 };
103056
103057         Object.defineProperties( AbstractNode, staticAccessors$22 );
103058
103059         var Collections = function Collections () {};
103060
103061         Collections.reverseOrder = function reverseOrder () {
103062           return {
103063             compare: function compare (a, b) {
103064               return b.compareTo(a)
103065             }
103066           }
103067         };
103068         Collections.min = function min (l) {
103069           Collections.sort(l);
103070           return l.get(0)
103071         };
103072         Collections.sort = function sort (l, c) {
103073           var a = l.toArray();
103074           if (c) {
103075             Arrays.sort(a, c);
103076           } else {
103077             Arrays.sort(a);
103078           }
103079           var i = l.iterator();
103080           for (var pos = 0, alen = a.length; pos < alen; pos++) {
103081             i.next();
103082             i.set(a[pos]);
103083           }
103084         };
103085         Collections.singletonList = function singletonList (o) {
103086           var arrayList = new ArrayList();
103087           arrayList.add(o);
103088           return arrayList
103089         };
103090
103091         var BoundablePair = function BoundablePair () {
103092           this._boundable1 = null;
103093           this._boundable2 = null;
103094           this._distance = null;
103095           this._itemDistance = null;
103096           var boundable1 = arguments[0];
103097           var boundable2 = arguments[1];
103098           var itemDistance = arguments[2];
103099           this._boundable1 = boundable1;
103100           this._boundable2 = boundable2;
103101           this._itemDistance = itemDistance;
103102           this._distance = this.distance();
103103         };
103104         BoundablePair.prototype.expandToQueue = function expandToQueue (priQ, minDistance) {
103105           var isComp1 = BoundablePair.isComposite(this._boundable1);
103106           var isComp2 = BoundablePair.isComposite(this._boundable2);
103107           if (isComp1 && isComp2) {
103108             if (BoundablePair.area(this._boundable1) > BoundablePair.area(this._boundable2)) {
103109               this.expand(this._boundable1, this._boundable2, priQ, minDistance);
103110               return null
103111             } else {
103112               this.expand(this._boundable2, this._boundable1, priQ, minDistance);
103113               return null
103114             }
103115           } else if (isComp1) {
103116             this.expand(this._boundable1, this._boundable2, priQ, minDistance);
103117             return null
103118           } else if (isComp2) {
103119             this.expand(this._boundable2, this._boundable1, priQ, minDistance);
103120             return null
103121           }
103122           throw new IllegalArgumentException('neither boundable is composite')
103123         };
103124         BoundablePair.prototype.isLeaves = function isLeaves () {
103125           return !(BoundablePair.isComposite(this._boundable1) || BoundablePair.isComposite(this._boundable2))
103126         };
103127         BoundablePair.prototype.compareTo = function compareTo (o) {
103128           var nd = o;
103129           if (this._distance < nd._distance) { return -1 }
103130           if (this._distance > nd._distance) { return 1 }
103131           return 0
103132         };
103133         BoundablePair.prototype.expand = function expand (bndComposite, bndOther, priQ, minDistance) {
103134             var this$1 = this;
103135
103136           var children = bndComposite.getChildBoundables();
103137           for (var i = children.iterator(); i.hasNext();) {
103138             var child = i.next();
103139             var bp = new BoundablePair(child, bndOther, this$1._itemDistance);
103140             if (bp.getDistance() < minDistance) {
103141               priQ.add(bp);
103142             }
103143           }
103144         };
103145         BoundablePair.prototype.getBoundable = function getBoundable (i) {
103146           if (i === 0) { return this._boundable1 }
103147           return this._boundable2
103148         };
103149         BoundablePair.prototype.getDistance = function getDistance () {
103150           return this._distance
103151         };
103152         BoundablePair.prototype.distance = function distance () {
103153           if (this.isLeaves()) {
103154             return this._itemDistance.distance(this._boundable1, this._boundable2)
103155           }
103156           return this._boundable1.getBounds().distance(this._boundable2.getBounds())
103157         };
103158         BoundablePair.prototype.interfaces_ = function interfaces_ () {
103159           return [Comparable]
103160         };
103161         BoundablePair.prototype.getClass = function getClass () {
103162           return BoundablePair
103163         };
103164         BoundablePair.area = function area (b) {
103165           return b.getBounds().getArea()
103166         };
103167         BoundablePair.isComposite = function isComposite (item) {
103168           return item instanceof AbstractNode
103169         };
103170
103171         var AbstractSTRtree = function AbstractSTRtree () {
103172           this._root = null;
103173           this._built = false;
103174           this._itemBoundables = new ArrayList();
103175           this._nodeCapacity = null;
103176           if (arguments.length === 0) {
103177             var nodeCapacity = AbstractSTRtree.DEFAULT_NODE_CAPACITY;
103178             this._nodeCapacity = nodeCapacity;
103179           } else if (arguments.length === 1) {
103180             var nodeCapacity$1 = arguments[0];
103181             Assert.isTrue(nodeCapacity$1 > 1, 'Node capacity must be greater than 1');
103182             this._nodeCapacity = nodeCapacity$1;
103183           }
103184         };
103185
103186         var staticAccessors$23 = { IntersectsOp: { configurable: true },serialVersionUID: { configurable: true },DEFAULT_NODE_CAPACITY: { configurable: true } };
103187         AbstractSTRtree.prototype.getNodeCapacity = function getNodeCapacity () {
103188           return this._nodeCapacity
103189         };
103190         AbstractSTRtree.prototype.lastNode = function lastNode (nodes) {
103191           return nodes.get(nodes.size() - 1)
103192         };
103193         AbstractSTRtree.prototype.size = function size () {
103194             var this$1 = this;
103195
103196           if (arguments.length === 0) {
103197             if (this.isEmpty()) {
103198               return 0
103199             }
103200             this.build();
103201             return this.size(this._root)
103202           } else if (arguments.length === 1) {
103203             var node = arguments[0];
103204             var size = 0;
103205             for (var i = node.getChildBoundables().iterator(); i.hasNext();) {
103206               var childBoundable = i.next();
103207               if (childBoundable instanceof AbstractNode) {
103208                 size += this$1.size(childBoundable);
103209               } else if (childBoundable instanceof ItemBoundable) {
103210                 size += 1;
103211               }
103212             }
103213             return size
103214           }
103215         };
103216         AbstractSTRtree.prototype.removeItem = function removeItem (node, item) {
103217           var childToRemove = null;
103218           for (var i = node.getChildBoundables().iterator(); i.hasNext();) {
103219             var childBoundable = i.next();
103220             if (childBoundable instanceof ItemBoundable) {
103221               if (childBoundable.getItem() === item) { childToRemove = childBoundable; }
103222             }
103223           }
103224           if (childToRemove !== null) {
103225             node.getChildBoundables().remove(childToRemove);
103226             return true
103227           }
103228           return false
103229         };
103230         AbstractSTRtree.prototype.itemsTree = function itemsTree () {
103231             var this$1 = this;
103232
103233           if (arguments.length === 0) {
103234             this.build();
103235             var valuesTree = this.itemsTree(this._root);
103236             if (valuesTree === null) { return new ArrayList() }
103237             return valuesTree
103238           } else if (arguments.length === 1) {
103239             var node = arguments[0];
103240             var valuesTreeForNode = new ArrayList();
103241             for (var i = node.getChildBoundables().iterator(); i.hasNext();) {
103242               var childBoundable = i.next();
103243               if (childBoundable instanceof AbstractNode) {
103244                 var valuesTreeForChild = this$1.itemsTree(childBoundable);
103245                 if (valuesTreeForChild !== null) { valuesTreeForNode.add(valuesTreeForChild); }
103246               } else if (childBoundable instanceof ItemBoundable) {
103247                 valuesTreeForNode.add(childBoundable.getItem());
103248               } else {
103249                 Assert.shouldNeverReachHere();
103250               }
103251             }
103252             if (valuesTreeForNode.size() <= 0) { return null }
103253             return valuesTreeForNode
103254           }
103255         };
103256         AbstractSTRtree.prototype.insert = function insert (bounds, item) {
103257           Assert.isTrue(!this._built, 'Cannot insert items into an STR packed R-tree after it has been built.');
103258           this._itemBoundables.add(new ItemBoundable(bounds, item));
103259         };
103260         AbstractSTRtree.prototype.boundablesAtLevel = function boundablesAtLevel () {
103261             var this$1 = this;
103262
103263           if (arguments.length === 1) {
103264             var level = arguments[0];
103265             var boundables = new ArrayList();
103266             this.boundablesAtLevel(level, this._root, boundables);
103267             return boundables
103268           } else if (arguments.length === 3) {
103269             var level$1 = arguments[0];
103270             var top = arguments[1];
103271             var boundables$1 = arguments[2];
103272             Assert.isTrue(level$1 > -2);
103273             if (top.getLevel() === level$1) {
103274               boundables$1.add(top);
103275               return null
103276             }
103277             for (var i = top.getChildBoundables().iterator(); i.hasNext();) {
103278               var boundable = i.next();
103279               if (boundable instanceof AbstractNode) {
103280                 this$1.boundablesAtLevel(level$1, boundable, boundables$1);
103281               } else {
103282                 Assert.isTrue(boundable instanceof ItemBoundable);
103283                 if (level$1 === -1) {
103284                   boundables$1.add(boundable);
103285                 }
103286               }
103287             }
103288             return null
103289           }
103290         };
103291         AbstractSTRtree.prototype.query = function query () {
103292             var this$1 = this;
103293
103294           if (arguments.length === 1) {
103295             var searchBounds = arguments[0];
103296             this.build();
103297             var matches = new ArrayList();
103298             if (this.isEmpty()) {
103299               return matches
103300             }
103301             if (this.getIntersectsOp().intersects(this._root.getBounds(), searchBounds)) {
103302               this.query(searchBounds, this._root, matches);
103303             }
103304             return matches
103305           } else if (arguments.length === 2) {
103306             var searchBounds$1 = arguments[0];
103307             var visitor = arguments[1];
103308             this.build();
103309             if (this.isEmpty()) {
103310               return null
103311             }
103312             if (this.getIntersectsOp().intersects(this._root.getBounds(), searchBounds$1)) {
103313               this.query(searchBounds$1, this._root, visitor);
103314             }
103315           } else if (arguments.length === 3) {
103316             if (hasInterface(arguments[2], ItemVisitor) && (arguments[0] instanceof Object && arguments[1] instanceof AbstractNode)) {
103317               var searchBounds$2 = arguments[0];
103318               var node = arguments[1];
103319               var visitor$1 = arguments[2];
103320               var childBoundables = node.getChildBoundables();
103321               for (var i = 0; i < childBoundables.size(); i++) {
103322                 var childBoundable = childBoundables.get(i);
103323                 if (!this$1.getIntersectsOp().intersects(childBoundable.getBounds(), searchBounds$2)) {
103324                   continue
103325                 }
103326                 if (childBoundable instanceof AbstractNode) {
103327                   this$1.query(searchBounds$2, childBoundable, visitor$1);
103328                 } else if (childBoundable instanceof ItemBoundable) {
103329                   visitor$1.visitItem(childBoundable.getItem());
103330                 } else {
103331                   Assert.shouldNeverReachHere();
103332                 }
103333               }
103334             } else if (hasInterface(arguments[2], List) && (arguments[0] instanceof Object && arguments[1] instanceof AbstractNode)) {
103335               var searchBounds$3 = arguments[0];
103336               var node$1 = arguments[1];
103337               var matches$1 = arguments[2];
103338               var childBoundables$1 = node$1.getChildBoundables();
103339               for (var i$1 = 0; i$1 < childBoundables$1.size(); i$1++) {
103340                 var childBoundable$1 = childBoundables$1.get(i$1);
103341                 if (!this$1.getIntersectsOp().intersects(childBoundable$1.getBounds(), searchBounds$3)) {
103342                   continue
103343                 }
103344                 if (childBoundable$1 instanceof AbstractNode) {
103345                   this$1.query(searchBounds$3, childBoundable$1, matches$1);
103346                 } else if (childBoundable$1 instanceof ItemBoundable) {
103347                   matches$1.add(childBoundable$1.getItem());
103348                 } else {
103349                   Assert.shouldNeverReachHere();
103350                 }
103351               }
103352             }
103353           }
103354         };
103355         AbstractSTRtree.prototype.build = function build () {
103356           if (this._built) { return null }
103357           this._root = this._itemBoundables.isEmpty() ? this.createNode(0) : this.createHigherLevels(this._itemBoundables, -1);
103358           this._itemBoundables = null;
103359           this._built = true;
103360         };
103361         AbstractSTRtree.prototype.getRoot = function getRoot () {
103362           this.build();
103363           return this._root
103364         };
103365         AbstractSTRtree.prototype.remove = function remove () {
103366             var this$1 = this;
103367
103368           if (arguments.length === 2) {
103369             var searchBounds = arguments[0];
103370             var item = arguments[1];
103371             this.build();
103372             if (this.getIntersectsOp().intersects(this._root.getBounds(), searchBounds)) {
103373               return this.remove(searchBounds, this._root, item)
103374             }
103375             return false
103376           } else if (arguments.length === 3) {
103377             var searchBounds$1 = arguments[0];
103378             var node = arguments[1];
103379             var item$1 = arguments[2];
103380             var found = this.removeItem(node, item$1);
103381             if (found) { return true }
103382             var childToPrune = null;
103383             for (var i = node.getChildBoundables().iterator(); i.hasNext();) {
103384               var childBoundable = i.next();
103385               if (!this$1.getIntersectsOp().intersects(childBoundable.getBounds(), searchBounds$1)) {
103386                 continue
103387               }
103388               if (childBoundable instanceof AbstractNode) {
103389                 found = this$1.remove(searchBounds$1, childBoundable, item$1);
103390                 if (found) {
103391                   childToPrune = childBoundable;
103392                   break
103393                 }
103394               }
103395             }
103396             if (childToPrune !== null) {
103397               if (childToPrune.getChildBoundables().isEmpty()) {
103398                 node.getChildBoundables().remove(childToPrune);
103399               }
103400             }
103401             return found
103402           }
103403         };
103404         AbstractSTRtree.prototype.createHigherLevels = function createHigherLevels (boundablesOfALevel, level) {
103405           Assert.isTrue(!boundablesOfALevel.isEmpty());
103406           var parentBoundables = this.createParentBoundables(boundablesOfALevel, level + 1);
103407           if (parentBoundables.size() === 1) {
103408             return parentBoundables.get(0)
103409           }
103410           return this.createHigherLevels(parentBoundables, level + 1)
103411         };
103412         AbstractSTRtree.prototype.depth = function depth () {
103413             var this$1 = this;
103414
103415           if (arguments.length === 0) {
103416             if (this.isEmpty()) {
103417               return 0
103418             }
103419             this.build();
103420             return this.depth(this._root)
103421           } else if (arguments.length === 1) {
103422             var node = arguments[0];
103423             var maxChildDepth = 0;
103424             for (var i = node.getChildBoundables().iterator(); i.hasNext();) {
103425               var childBoundable = i.next();
103426               if (childBoundable instanceof AbstractNode) {
103427                 var childDepth = this$1.depth(childBoundable);
103428                 if (childDepth > maxChildDepth) { maxChildDepth = childDepth; }
103429               }
103430             }
103431             return maxChildDepth + 1
103432           }
103433         };
103434         AbstractSTRtree.prototype.createParentBoundables = function createParentBoundables (childBoundables, newLevel) {
103435             var this$1 = this;
103436
103437           Assert.isTrue(!childBoundables.isEmpty());
103438           var parentBoundables = new ArrayList();
103439           parentBoundables.add(this.createNode(newLevel));
103440           var sortedChildBoundables = new ArrayList(childBoundables);
103441           Collections.sort(sortedChildBoundables, this.getComparator());
103442           for (var i = sortedChildBoundables.iterator(); i.hasNext();) {
103443             var childBoundable = i.next();
103444             if (this$1.lastNode(parentBoundables).getChildBoundables().size() === this$1.getNodeCapacity()) {
103445               parentBoundables.add(this$1.createNode(newLevel));
103446             }
103447             this$1.lastNode(parentBoundables).addChildBoundable(childBoundable);
103448           }
103449           return parentBoundables
103450         };
103451         AbstractSTRtree.prototype.isEmpty = function isEmpty () {
103452           if (!this._built) { return this._itemBoundables.isEmpty() }
103453           return this._root.isEmpty()
103454         };
103455         AbstractSTRtree.prototype.interfaces_ = function interfaces_ () {
103456           return [Serializable]
103457         };
103458         AbstractSTRtree.prototype.getClass = function getClass () {
103459           return AbstractSTRtree
103460         };
103461         AbstractSTRtree.compareDoubles = function compareDoubles (a, b) {
103462           return a > b ? 1 : a < b ? -1 : 0
103463         };
103464         staticAccessors$23.IntersectsOp.get = function () { return IntersectsOp };
103465         staticAccessors$23.serialVersionUID.get = function () { return -3886435814360241337 };
103466         staticAccessors$23.DEFAULT_NODE_CAPACITY.get = function () { return 10 };
103467
103468         Object.defineProperties( AbstractSTRtree, staticAccessors$23 );
103469
103470         var IntersectsOp = function IntersectsOp () {};
103471
103472         var ItemDistance = function ItemDistance () {};
103473
103474         ItemDistance.prototype.distance = function distance (item1, item2) {};
103475         ItemDistance.prototype.interfaces_ = function interfaces_ () {
103476           return []
103477         };
103478         ItemDistance.prototype.getClass = function getClass () {
103479           return ItemDistance
103480         };
103481
103482         var STRtree = (function (AbstractSTRtree$$1) {
103483           function STRtree (nodeCapacity) {
103484             nodeCapacity = nodeCapacity || STRtree.DEFAULT_NODE_CAPACITY;
103485             AbstractSTRtree$$1.call(this, nodeCapacity);
103486           }
103487
103488           if ( AbstractSTRtree$$1 ) { STRtree.__proto__ = AbstractSTRtree$$1; }
103489           STRtree.prototype = Object.create( AbstractSTRtree$$1 && AbstractSTRtree$$1.prototype );
103490           STRtree.prototype.constructor = STRtree;
103491
103492           var staticAccessors = { STRtreeNode: { configurable: true },serialVersionUID: { configurable: true },xComparator: { configurable: true },yComparator: { configurable: true },intersectsOp: { configurable: true },DEFAULT_NODE_CAPACITY: { configurable: true } };
103493           STRtree.prototype.createParentBoundablesFromVerticalSlices = function createParentBoundablesFromVerticalSlices (verticalSlices, newLevel) {
103494             var this$1 = this;
103495
103496             Assert.isTrue(verticalSlices.length > 0);
103497             var parentBoundables = new ArrayList();
103498             for (var i = 0; i < verticalSlices.length; i++) {
103499               parentBoundables.addAll(this$1.createParentBoundablesFromVerticalSlice(verticalSlices[i], newLevel));
103500             }
103501             return parentBoundables
103502           };
103503           STRtree.prototype.createNode = function createNode (level) {
103504             return new STRtreeNode(level)
103505           };
103506           STRtree.prototype.size = function size () {
103507             if (arguments.length === 0) {
103508               return AbstractSTRtree$$1.prototype.size.call(this)
103509             } else { return AbstractSTRtree$$1.prototype.size.apply(this, arguments) }
103510           };
103511           STRtree.prototype.insert = function insert () {
103512             if (arguments.length === 2) {
103513               var itemEnv = arguments[0];
103514               var item = arguments[1];
103515               if (itemEnv.isNull()) {
103516                 return null
103517               }
103518               AbstractSTRtree$$1.prototype.insert.call(this, itemEnv, item);
103519             } else { return AbstractSTRtree$$1.prototype.insert.apply(this, arguments) }
103520           };
103521           STRtree.prototype.getIntersectsOp = function getIntersectsOp () {
103522             return STRtree.intersectsOp
103523           };
103524           STRtree.prototype.verticalSlices = function verticalSlices (childBoundables, sliceCount) {
103525             var sliceCapacity = Math.trunc(Math.ceil(childBoundables.size() / sliceCount));
103526             var slices = new Array(sliceCount).fill(null);
103527             var i = childBoundables.iterator();
103528             for (var j = 0; j < sliceCount; j++) {
103529               slices[j] = new ArrayList();
103530               var boundablesAddedToSlice = 0;
103531               while (i.hasNext() && boundablesAddedToSlice < sliceCapacity) {
103532                 var childBoundable = i.next();
103533                 slices[j].add(childBoundable);
103534                 boundablesAddedToSlice++;
103535               }
103536             }
103537             return slices
103538           };
103539           STRtree.prototype.query = function query () {
103540             if (arguments.length === 1) {
103541               var searchEnv = arguments[0];
103542               return AbstractSTRtree$$1.prototype.query.call(this, searchEnv)
103543             } else if (arguments.length === 2) {
103544               var searchEnv$1 = arguments[0];
103545               var visitor = arguments[1];
103546               AbstractSTRtree$$1.prototype.query.call(this, searchEnv$1, visitor);
103547             } else if (arguments.length === 3) {
103548               if (hasInterface(arguments[2], ItemVisitor) && (arguments[0] instanceof Object && arguments[1] instanceof AbstractNode)) {
103549                 var searchBounds = arguments[0];
103550                 var node = arguments[1];
103551                 var visitor$1 = arguments[2];
103552                 AbstractSTRtree$$1.prototype.query.call(this, searchBounds, node, visitor$1);
103553               } else if (hasInterface(arguments[2], List) && (arguments[0] instanceof Object && arguments[1] instanceof AbstractNode)) {
103554                 var searchBounds$1 = arguments[0];
103555                 var node$1 = arguments[1];
103556                 var matches = arguments[2];
103557                 AbstractSTRtree$$1.prototype.query.call(this, searchBounds$1, node$1, matches);
103558               }
103559             }
103560           };
103561           STRtree.prototype.getComparator = function getComparator () {
103562             return STRtree.yComparator
103563           };
103564           STRtree.prototype.createParentBoundablesFromVerticalSlice = function createParentBoundablesFromVerticalSlice (childBoundables, newLevel) {
103565             return AbstractSTRtree$$1.prototype.createParentBoundables.call(this, childBoundables, newLevel)
103566           };
103567           STRtree.prototype.remove = function remove () {
103568             if (arguments.length === 2) {
103569               var itemEnv = arguments[0];
103570               var item = arguments[1];
103571               return AbstractSTRtree$$1.prototype.remove.call(this, itemEnv, item)
103572             } else { return AbstractSTRtree$$1.prototype.remove.apply(this, arguments) }
103573           };
103574           STRtree.prototype.depth = function depth () {
103575             if (arguments.length === 0) {
103576               return AbstractSTRtree$$1.prototype.depth.call(this)
103577             } else { return AbstractSTRtree$$1.prototype.depth.apply(this, arguments) }
103578           };
103579           STRtree.prototype.createParentBoundables = function createParentBoundables (childBoundables, newLevel) {
103580             Assert.isTrue(!childBoundables.isEmpty());
103581             var minLeafCount = Math.trunc(Math.ceil(childBoundables.size() / this.getNodeCapacity()));
103582             var sortedChildBoundables = new ArrayList(childBoundables);
103583             Collections.sort(sortedChildBoundables, STRtree.xComparator);
103584             var verticalSlices = this.verticalSlices(sortedChildBoundables, Math.trunc(Math.ceil(Math.sqrt(minLeafCount))));
103585             return this.createParentBoundablesFromVerticalSlices(verticalSlices, newLevel)
103586           };
103587           STRtree.prototype.nearestNeighbour = function nearestNeighbour () {
103588             if (arguments.length === 1) {
103589               if (hasInterface(arguments[0], ItemDistance)) {
103590                 var itemDist = arguments[0];
103591                 var bp = new BoundablePair(this.getRoot(), this.getRoot(), itemDist);
103592                 return this.nearestNeighbour(bp)
103593               } else if (arguments[0] instanceof BoundablePair) {
103594                 var initBndPair = arguments[0];
103595                 return this.nearestNeighbour(initBndPair, Double.POSITIVE_INFINITY)
103596               }
103597             } else if (arguments.length === 2) {
103598               if (arguments[0] instanceof STRtree && hasInterface(arguments[1], ItemDistance)) {
103599                 var tree = arguments[0];
103600                 var itemDist$1 = arguments[1];
103601                 var bp$1 = new BoundablePair(this.getRoot(), tree.getRoot(), itemDist$1);
103602                 return this.nearestNeighbour(bp$1)
103603               } else if (arguments[0] instanceof BoundablePair && typeof arguments[1] === 'number') {
103604                 var initBndPair$1 = arguments[0];
103605                 var maxDistance = arguments[1];
103606                 var distanceLowerBound = maxDistance;
103607                 var minPair = null;
103608                 var priQ = new PriorityQueue();
103609                 priQ.add(initBndPair$1);
103610                 while (!priQ.isEmpty() && distanceLowerBound > 0.0) {
103611                   var bndPair = priQ.poll();
103612                   var currentDistance = bndPair.getDistance();
103613                   if (currentDistance >= distanceLowerBound) { break }
103614                   if (bndPair.isLeaves()) {
103615                     distanceLowerBound = currentDistance;
103616                     minPair = bndPair;
103617                   } else {
103618                     bndPair.expandToQueue(priQ, distanceLowerBound);
103619                   }
103620                 }
103621                 return [minPair.getBoundable(0).getItem(), minPair.getBoundable(1).getItem()]
103622               }
103623             } else if (arguments.length === 3) {
103624               var env = arguments[0];
103625               var item = arguments[1];
103626               var itemDist$2 = arguments[2];
103627               var bnd = new ItemBoundable(env, item);
103628               var bp$2 = new BoundablePair(this.getRoot(), bnd, itemDist$2);
103629               return this.nearestNeighbour(bp$2)[0]
103630             }
103631           };
103632           STRtree.prototype.interfaces_ = function interfaces_ () {
103633             return [SpatialIndex, Serializable]
103634           };
103635           STRtree.prototype.getClass = function getClass () {
103636             return STRtree
103637           };
103638           STRtree.centreX = function centreX (e) {
103639             return STRtree.avg(e.getMinX(), e.getMaxX())
103640           };
103641           STRtree.avg = function avg (a, b) {
103642             return (a + b) / 2
103643           };
103644           STRtree.centreY = function centreY (e) {
103645             return STRtree.avg(e.getMinY(), e.getMaxY())
103646           };
103647           staticAccessors.STRtreeNode.get = function () { return STRtreeNode };
103648           staticAccessors.serialVersionUID.get = function () { return 259274702368956900 };
103649           staticAccessors.xComparator.get = function () {
103650             return {
103651               interfaces_: function () {
103652                 return [Comparator]
103653               },
103654               compare: function (o1, o2) {
103655                 return AbstractSTRtree$$1.compareDoubles(STRtree.centreX(o1.getBounds()), STRtree.centreX(o2.getBounds()))
103656               }
103657             }
103658           };
103659           staticAccessors.yComparator.get = function () {
103660             return {
103661               interfaces_: function () {
103662                 return [Comparator]
103663               },
103664               compare: function (o1, o2) {
103665                 return AbstractSTRtree$$1.compareDoubles(STRtree.centreY(o1.getBounds()), STRtree.centreY(o2.getBounds()))
103666               }
103667             }
103668           };
103669           staticAccessors.intersectsOp.get = function () {
103670             return {
103671               interfaces_: function () {
103672                 return [AbstractSTRtree$$1.IntersectsOp]
103673               },
103674               intersects: function (aBounds, bBounds) {
103675                 return aBounds.intersects(bBounds)
103676               }
103677             }
103678           };
103679           staticAccessors.DEFAULT_NODE_CAPACITY.get = function () { return 10 };
103680
103681           Object.defineProperties( STRtree, staticAccessors );
103682
103683           return STRtree;
103684         }(AbstractSTRtree));
103685
103686         var STRtreeNode = (function (AbstractNode$$1) {
103687           function STRtreeNode () {
103688             var level = arguments[0];
103689             AbstractNode$$1.call(this, level);
103690           }
103691
103692           if ( AbstractNode$$1 ) { STRtreeNode.__proto__ = AbstractNode$$1; }
103693           STRtreeNode.prototype = Object.create( AbstractNode$$1 && AbstractNode$$1.prototype );
103694           STRtreeNode.prototype.constructor = STRtreeNode;
103695           STRtreeNode.prototype.computeBounds = function computeBounds () {
103696             var bounds = null;
103697             for (var i = this.getChildBoundables().iterator(); i.hasNext();) {
103698               var childBoundable = i.next();
103699               if (bounds === null) {
103700                 bounds = new Envelope(childBoundable.getBounds());
103701               } else {
103702                 bounds.expandToInclude(childBoundable.getBounds());
103703               }
103704             }
103705             return bounds
103706           };
103707           STRtreeNode.prototype.interfaces_ = function interfaces_ () {
103708             return []
103709           };
103710           STRtreeNode.prototype.getClass = function getClass () {
103711             return STRtreeNode
103712           };
103713
103714           return STRtreeNode;
103715         }(AbstractNode));
103716
103717         var SegmentPointComparator = function SegmentPointComparator () {};
103718
103719         SegmentPointComparator.prototype.interfaces_ = function interfaces_ () {
103720           return []
103721         };
103722         SegmentPointComparator.prototype.getClass = function getClass () {
103723           return SegmentPointComparator
103724         };
103725         SegmentPointComparator.relativeSign = function relativeSign (x0, x1) {
103726           if (x0 < x1) { return -1 }
103727           if (x0 > x1) { return 1 }
103728           return 0
103729         };
103730         SegmentPointComparator.compare = function compare (octant, p0, p1) {
103731           if (p0.equals2D(p1)) { return 0 }
103732           var xSign = SegmentPointComparator.relativeSign(p0.x, p1.x);
103733           var ySign = SegmentPointComparator.relativeSign(p0.y, p1.y);
103734           switch (octant) {
103735             case 0:
103736               return SegmentPointComparator.compareValue(xSign, ySign)
103737             case 1:
103738               return SegmentPointComparator.compareValue(ySign, xSign)
103739             case 2:
103740               return SegmentPointComparator.compareValue(ySign, -xSign)
103741             case 3:
103742               return SegmentPointComparator.compareValue(-xSign, ySign)
103743             case 4:
103744               return SegmentPointComparator.compareValue(-xSign, -ySign)
103745             case 5:
103746               return SegmentPointComparator.compareValue(-ySign, -xSign)
103747             case 6:
103748               return SegmentPointComparator.compareValue(-ySign, xSign)
103749             case 7:
103750               return SegmentPointComparator.compareValue(xSign, -ySign)
103751           }
103752           Assert.shouldNeverReachHere('invalid octant value');
103753           return 0
103754         };
103755         SegmentPointComparator.compareValue = function compareValue (compareSign0, compareSign1) {
103756           if (compareSign0 < 0) { return -1 }
103757           if (compareSign0 > 0) { return 1 }
103758           if (compareSign1 < 0) { return -1 }
103759           if (compareSign1 > 0) { return 1 }
103760           return 0
103761         };
103762
103763         var SegmentNode = function SegmentNode () {
103764           this._segString = null;
103765           this.coord = null;
103766           this.segmentIndex = null;
103767           this._segmentOctant = null;
103768           this._isInterior = null;
103769           var segString = arguments[0];
103770           var coord = arguments[1];
103771           var segmentIndex = arguments[2];
103772           var segmentOctant = arguments[3];
103773           this._segString = segString;
103774           this.coord = new Coordinate(coord);
103775           this.segmentIndex = segmentIndex;
103776           this._segmentOctant = segmentOctant;
103777           this._isInterior = !coord.equals2D(segString.getCoordinate(segmentIndex));
103778         };
103779         SegmentNode.prototype.getCoordinate = function getCoordinate () {
103780           return this.coord
103781         };
103782         SegmentNode.prototype.print = function print (out) {
103783           out.print(this.coord);
103784           out.print(' seg # = ' + this.segmentIndex);
103785         };
103786         SegmentNode.prototype.compareTo = function compareTo (obj) {
103787           var other = obj;
103788           if (this.segmentIndex < other.segmentIndex) { return -1 }
103789           if (this.segmentIndex > other.segmentIndex) { return 1 }
103790           if (this.coord.equals2D(other.coord)) { return 0 }
103791           return SegmentPointComparator.compare(this._segmentOctant, this.coord, other.coord)
103792         };
103793         SegmentNode.prototype.isEndPoint = function isEndPoint (maxSegmentIndex) {
103794           if (this.segmentIndex === 0 && !this._isInterior) { return true }
103795           if (this.segmentIndex === maxSegmentIndex) { return true }
103796           return false
103797         };
103798         SegmentNode.prototype.isInterior = function isInterior () {
103799           return this._isInterior
103800         };
103801         SegmentNode.prototype.interfaces_ = function interfaces_ () {
103802           return [Comparable]
103803         };
103804         SegmentNode.prototype.getClass = function getClass () {
103805           return SegmentNode
103806         };
103807
103808         // import Iterator from '../../../../java/util/Iterator'
103809         var SegmentNodeList = function SegmentNodeList () {
103810           this._nodeMap = new TreeMap();
103811           this._edge = null;
103812           var edge = arguments[0];
103813           this._edge = edge;
103814         };
103815         SegmentNodeList.prototype.getSplitCoordinates = function getSplitCoordinates () {
103816             var this$1 = this;
103817
103818           var coordList = new CoordinateList();
103819           this.addEndpoints();
103820           var it = this.iterator();
103821           var eiPrev = it.next();
103822           while (it.hasNext()) {
103823             var ei = it.next();
103824             this$1.addEdgeCoordinates(eiPrev, ei, coordList);
103825             eiPrev = ei;
103826           }
103827           return coordList.toCoordinateArray()
103828         };
103829         SegmentNodeList.prototype.addCollapsedNodes = function addCollapsedNodes () {
103830             var this$1 = this;
103831
103832           var collapsedVertexIndexes = new ArrayList();
103833           this.findCollapsesFromInsertedNodes(collapsedVertexIndexes);
103834           this.findCollapsesFromExistingVertices(collapsedVertexIndexes);
103835           for (var it = collapsedVertexIndexes.iterator(); it.hasNext();) {
103836             var vertexIndex = it.next().intValue();
103837             this$1.add(this$1._edge.getCoordinate(vertexIndex), vertexIndex);
103838           }
103839         };
103840         SegmentNodeList.prototype.print = function print (out) {
103841           out.println('Intersections:');
103842           for (var it = this.iterator(); it.hasNext();) {
103843             var ei = it.next();
103844             ei.print(out);
103845           }
103846         };
103847         SegmentNodeList.prototype.findCollapsesFromExistingVertices = function findCollapsesFromExistingVertices (collapsedVertexIndexes) {
103848             var this$1 = this;
103849
103850           for (var i = 0; i < this._edge.size() - 2; i++) {
103851             var p0 = this$1._edge.getCoordinate(i);
103852             // const p1 = this._edge.getCoordinate(i + 1)
103853             var p2 = this$1._edge.getCoordinate(i + 2);
103854             if (p0.equals2D(p2)) {
103855               collapsedVertexIndexes.add(new Integer(i + 1));
103856             }
103857           }
103858         };
103859         SegmentNodeList.prototype.addEdgeCoordinates = function addEdgeCoordinates (ei0, ei1, coordList) {
103860             var this$1 = this;
103861
103862           // let npts = ei1.segmentIndex - ei0.segmentIndex + 2
103863           var lastSegStartPt = this._edge.getCoordinate(ei1.segmentIndex);
103864           var useIntPt1 = ei1.isInterior() || !ei1.coord.equals2D(lastSegStartPt);
103865           // if (!useIntPt1) {
103866           // npts--
103867           // }
103868           // const ipt = 0
103869           coordList.add(new Coordinate(ei0.coord), false);
103870           for (var i = ei0.segmentIndex + 1; i <= ei1.segmentIndex; i++) {
103871             coordList.add(this$1._edge.getCoordinate(i));
103872           }
103873           if (useIntPt1) {
103874             coordList.add(new Coordinate(ei1.coord));
103875           }
103876         };
103877         SegmentNodeList.prototype.iterator = function iterator () {
103878           return this._nodeMap.values().iterator()
103879         };
103880         SegmentNodeList.prototype.addSplitEdges = function addSplitEdges (edgeList) {
103881             var this$1 = this;
103882
103883           this.addEndpoints();
103884           this.addCollapsedNodes();
103885           var it = this.iterator();
103886           var eiPrev = it.next();
103887           while (it.hasNext()) {
103888             var ei = it.next();
103889             var newEdge = this$1.createSplitEdge(eiPrev, ei);
103890             edgeList.add(newEdge);
103891             eiPrev = ei;
103892           }
103893         };
103894         SegmentNodeList.prototype.findCollapseIndex = function findCollapseIndex (ei0, ei1, collapsedVertexIndex) {
103895           if (!ei0.coord.equals2D(ei1.coord)) { return false }
103896           var numVerticesBetween = ei1.segmentIndex - ei0.segmentIndex;
103897           if (!ei1.isInterior()) {
103898             numVerticesBetween--;
103899           }
103900           if (numVerticesBetween === 1) {
103901             collapsedVertexIndex[0] = ei0.segmentIndex + 1;
103902             return true
103903           }
103904           return false
103905         };
103906         SegmentNodeList.prototype.findCollapsesFromInsertedNodes = function findCollapsesFromInsertedNodes (collapsedVertexIndexes) {
103907             var this$1 = this;
103908
103909           var collapsedVertexIndex = new Array(1).fill(null);
103910           var it = this.iterator();
103911           var eiPrev = it.next();
103912           while (it.hasNext()) {
103913             var ei = it.next();
103914             var isCollapsed = this$1.findCollapseIndex(eiPrev, ei, collapsedVertexIndex);
103915             if (isCollapsed) { collapsedVertexIndexes.add(new Integer(collapsedVertexIndex[0])); }
103916             eiPrev = ei;
103917           }
103918         };
103919         SegmentNodeList.prototype.getEdge = function getEdge () {
103920           return this._edge
103921         };
103922         SegmentNodeList.prototype.addEndpoints = function addEndpoints () {
103923           var maxSegIndex = this._edge.size() - 1;
103924           this.add(this._edge.getCoordinate(0), 0);
103925           this.add(this._edge.getCoordinate(maxSegIndex), maxSegIndex);
103926         };
103927         SegmentNodeList.prototype.createSplitEdge = function createSplitEdge (ei0, ei1) {
103928             var this$1 = this;
103929
103930           var npts = ei1.segmentIndex - ei0.segmentIndex + 2;
103931           var lastSegStartPt = this._edge.getCoordinate(ei1.segmentIndex);
103932           var useIntPt1 = ei1.isInterior() || !ei1.coord.equals2D(lastSegStartPt);
103933           if (!useIntPt1) {
103934             npts--;
103935           }
103936           var pts = new Array(npts).fill(null);
103937           var ipt = 0;
103938           pts[ipt++] = new Coordinate(ei0.coord);
103939           for (var i = ei0.segmentIndex + 1; i <= ei1.segmentIndex; i++) {
103940             pts[ipt++] = this$1._edge.getCoordinate(i);
103941           }
103942           if (useIntPt1) { pts[ipt] = new Coordinate(ei1.coord); }
103943           return new NodedSegmentString(pts, this._edge.getData())
103944         };
103945         SegmentNodeList.prototype.add = function add (intPt, segmentIndex) {
103946           var eiNew = new SegmentNode(this._edge, intPt, segmentIndex, this._edge.getSegmentOctant(segmentIndex));
103947           var ei = this._nodeMap.get(eiNew);
103948           if (ei !== null) {
103949             Assert.isTrue(ei.coord.equals2D(intPt), 'Found equal nodes with different coordinates');
103950             return ei
103951           }
103952           this._nodeMap.put(eiNew, eiNew);
103953           return eiNew
103954         };
103955         SegmentNodeList.prototype.checkSplitEdgesCorrectness = function checkSplitEdgesCorrectness (splitEdges) {
103956           var edgePts = this._edge.getCoordinates();
103957           var split0 = splitEdges.get(0);
103958           var pt0 = split0.getCoordinate(0);
103959           if (!pt0.equals2D(edgePts[0])) { throw new RuntimeException('bad split edge start point at ' + pt0) }
103960           var splitn = splitEdges.get(splitEdges.size() - 1);
103961           var splitnPts = splitn.getCoordinates();
103962           var ptn = splitnPts[splitnPts.length - 1];
103963           if (!ptn.equals2D(edgePts[edgePts.length - 1])) { throw new RuntimeException('bad split edge end point at ' + ptn) }
103964         };
103965         SegmentNodeList.prototype.interfaces_ = function interfaces_ () {
103966           return []
103967         };
103968         SegmentNodeList.prototype.getClass = function getClass () {
103969           return SegmentNodeList
103970         };
103971
103972
103973
103974         // class NodeVertexIterator {
103975         //   constructor () {
103976         //     this._nodeList = null
103977         //     this._edge = null
103978         //     this._nodeIt = null
103979         //     this._currNode = null
103980         //     this._nextNode = null
103981         //     this._currSegIndex = 0
103982         //     let nodeList = arguments[0]
103983         //     this._nodeList = nodeList
103984         //     this._edge = nodeList.getEdge()
103985         //     this._nodeIt = nodeList.iterator()
103986         //     this.readNextNode()
103987         //   }
103988         //   next () {
103989         //     if (this._currNode === null) {
103990         //       this._currNode = this._nextNode
103991         //       this._currSegIndex = this._currNode.segmentIndex
103992         //       this.readNextNode()
103993         //       return this._currNode
103994         //     }
103995         //     if (this._nextNode === null) return null
103996         //     if (this._nextNode.segmentIndex === this._currNode.segmentIndex) {
103997         //       this._currNode = this._nextNode
103998         //       this._currSegIndex = this._currNode.segmentIndex
103999         //       this.readNextNode()
104000         //       return this._currNode
104001         //     }
104002         //     if (this._nextNode.segmentIndex > this._currNode.segmentIndex) {}
104003         //     return null
104004         //   }
104005         //   remove () {
104006         //     // throw new UnsupportedOperationException(this.getClass().getName())
104007         //   }
104008         //   hasNext () {
104009         //     if (this._nextNode === null) return false
104010         //     return true
104011         //   }
104012         //   readNextNode () {
104013         //     if (this._nodeIt.hasNext()) this._nextNode = this._nodeIt.next(); else this._nextNode = null
104014         //   }
104015         //   interfaces_ () {
104016         //     return [Iterator]
104017         //   }
104018         //   getClass () {
104019         //     return NodeVertexIterator
104020         //   }
104021         // }
104022
104023         var Octant = function Octant () {};
104024
104025         Octant.prototype.interfaces_ = function interfaces_ () {
104026           return []
104027         };
104028         Octant.prototype.getClass = function getClass () {
104029           return Octant
104030         };
104031         Octant.octant = function octant () {
104032           if (typeof arguments[0] === 'number' && typeof arguments[1] === 'number') {
104033             var dx = arguments[0];
104034             var dy = arguments[1];
104035             if (dx === 0.0 && dy === 0.0) { throw new IllegalArgumentException('Cannot compute the octant for point ( ' + dx + ', ' + dy + ' )') }
104036             var adx = Math.abs(dx);
104037             var ady = Math.abs(dy);
104038             if (dx >= 0) {
104039               if (dy >= 0) {
104040                 if (adx >= ady) { return 0; } else { return 1 }
104041               } else {
104042                 if (adx >= ady) { return 7; } else { return 6 }
104043               }
104044             } else {
104045               if (dy >= 0) {
104046                 if (adx >= ady) { return 3; } else { return 2 }
104047               } else {
104048                 if (adx >= ady) { return 4; } else { return 5 }
104049               }
104050             }
104051           } else if (arguments[0] instanceof Coordinate && arguments[1] instanceof Coordinate) {
104052             var p0 = arguments[0];
104053             var p1 = arguments[1];
104054             var dx$1 = p1.x - p0.x;
104055             var dy$1 = p1.y - p0.y;
104056             if (dx$1 === 0.0 && dy$1 === 0.0) { throw new IllegalArgumentException('Cannot compute the octant for two identical points ' + p0) }
104057             return Octant.octant(dx$1, dy$1)
104058           }
104059         };
104060
104061         var SegmentString = function SegmentString () {};
104062
104063         SegmentString.prototype.getCoordinates = function getCoordinates () {};
104064         SegmentString.prototype.size = function size () {};
104065         SegmentString.prototype.getCoordinate = function getCoordinate (i) {};
104066         SegmentString.prototype.isClosed = function isClosed () {};
104067         SegmentString.prototype.setData = function setData (data) {};
104068         SegmentString.prototype.getData = function getData () {};
104069         SegmentString.prototype.interfaces_ = function interfaces_ () {
104070           return []
104071         };
104072         SegmentString.prototype.getClass = function getClass () {
104073           return SegmentString
104074         };
104075
104076         var NodableSegmentString = function NodableSegmentString () {};
104077
104078         NodableSegmentString.prototype.addIntersection = function addIntersection (intPt, segmentIndex) {};
104079         NodableSegmentString.prototype.interfaces_ = function interfaces_ () {
104080           return [SegmentString]
104081         };
104082         NodableSegmentString.prototype.getClass = function getClass () {
104083           return NodableSegmentString
104084         };
104085
104086         var NodedSegmentString = function NodedSegmentString () {
104087           this._nodeList = new SegmentNodeList(this);
104088           this._pts = null;
104089           this._data = null;
104090           var pts = arguments[0];
104091           var data = arguments[1];
104092           this._pts = pts;
104093           this._data = data;
104094         };
104095         NodedSegmentString.prototype.getCoordinates = function getCoordinates () {
104096           return this._pts
104097         };
104098         NodedSegmentString.prototype.size = function size () {
104099           return this._pts.length
104100         };
104101         NodedSegmentString.prototype.getCoordinate = function getCoordinate (i) {
104102           return this._pts[i]
104103         };
104104         NodedSegmentString.prototype.isClosed = function isClosed () {
104105           return this._pts[0].equals(this._pts[this._pts.length - 1])
104106         };
104107         NodedSegmentString.prototype.getSegmentOctant = function getSegmentOctant (index) {
104108           if (index === this._pts.length - 1) { return -1 }
104109           return this.safeOctant(this.getCoordinate(index), this.getCoordinate(index + 1))
104110         };
104111         NodedSegmentString.prototype.setData = function setData (data) {
104112           this._data = data;
104113         };
104114         NodedSegmentString.prototype.safeOctant = function safeOctant (p0, p1) {
104115           if (p0.equals2D(p1)) { return 0 }
104116           return Octant.octant(p0, p1)
104117         };
104118         NodedSegmentString.prototype.getData = function getData () {
104119           return this._data
104120         };
104121         NodedSegmentString.prototype.addIntersection = function addIntersection () {
104122           if (arguments.length === 2) {
104123             var intPt$1 = arguments[0];
104124             var segmentIndex = arguments[1];
104125             this.addIntersectionNode(intPt$1, segmentIndex);
104126           } else if (arguments.length === 4) {
104127             var li = arguments[0];
104128             var segmentIndex$1 = arguments[1];
104129             // const geomIndex = arguments[2]
104130             var intIndex = arguments[3];
104131             var intPt = new Coordinate(li.getIntersection(intIndex));
104132             this.addIntersection(intPt, segmentIndex$1);
104133           }
104134         };
104135         NodedSegmentString.prototype.toString = function toString () {
104136           return WKTWriter.toLineString(new CoordinateArraySequence(this._pts))
104137         };
104138         NodedSegmentString.prototype.getNodeList = function getNodeList () {
104139           return this._nodeList
104140         };
104141         NodedSegmentString.prototype.addIntersectionNode = function addIntersectionNode (intPt, segmentIndex) {
104142           var normalizedSegmentIndex = segmentIndex;
104143           var nextSegIndex = normalizedSegmentIndex + 1;
104144           if (nextSegIndex < this._pts.length) {
104145             var nextPt = this._pts[nextSegIndex];
104146             if (intPt.equals2D(nextPt)) {
104147               normalizedSegmentIndex = nextSegIndex;
104148             }
104149           }
104150           var ei = this._nodeList.add(intPt, normalizedSegmentIndex);
104151           return ei
104152         };
104153         NodedSegmentString.prototype.addIntersections = function addIntersections (li, segmentIndex, geomIndex) {
104154             var this$1 = this;
104155
104156           for (var i = 0; i < li.getIntersectionNum(); i++) {
104157             this$1.addIntersection(li, segmentIndex, geomIndex, i);
104158           }
104159         };
104160         NodedSegmentString.prototype.interfaces_ = function interfaces_ () {
104161           return [NodableSegmentString]
104162         };
104163         NodedSegmentString.prototype.getClass = function getClass () {
104164           return NodedSegmentString
104165         };
104166         NodedSegmentString.getNodedSubstrings = function getNodedSubstrings () {
104167           if (arguments.length === 1) {
104168             var segStrings = arguments[0];
104169             var resultEdgelist = new ArrayList();
104170             NodedSegmentString.getNodedSubstrings(segStrings, resultEdgelist);
104171             return resultEdgelist
104172           } else if (arguments.length === 2) {
104173             var segStrings$1 = arguments[0];
104174             var resultEdgelist$1 = arguments[1];
104175             for (var i = segStrings$1.iterator(); i.hasNext();) {
104176               var ss = i.next();
104177               ss.getNodeList().addSplitEdges(resultEdgelist$1);
104178             }
104179           }
104180         };
104181
104182         var LineSegment = function LineSegment () {
104183           this.p0 = null;
104184           this.p1 = null;
104185           if (arguments.length === 0) {
104186             this.p0 = new Coordinate();
104187             this.p1 = new Coordinate();
104188           } else if (arguments.length === 1) {
104189             var ls = arguments[0];
104190             this.p0 = new Coordinate(ls.p0);
104191             this.p1 = new Coordinate(ls.p1);
104192           } else if (arguments.length === 2) {
104193             this.p0 = arguments[0];
104194             this.p1 = arguments[1];
104195           } else if (arguments.length === 4) {
104196             var x0 = arguments[0];
104197             var y0 = arguments[1];
104198             var x1 = arguments[2];
104199             var y1 = arguments[3];
104200             this.p0 = new Coordinate(x0, y0);
104201             this.p1 = new Coordinate(x1, y1);
104202           }
104203         };
104204
104205         var staticAccessors$24 = { serialVersionUID: { configurable: true } };
104206         LineSegment.prototype.minX = function minX () {
104207           return Math.min(this.p0.x, this.p1.x)
104208         };
104209         LineSegment.prototype.orientationIndex = function orientationIndex () {
104210           if (arguments[0] instanceof LineSegment) {
104211             var seg = arguments[0];
104212             var orient0 = CGAlgorithms.orientationIndex(this.p0, this.p1, seg.p0);
104213             var orient1 = CGAlgorithms.orientationIndex(this.p0, this.p1, seg.p1);
104214             if (orient0 >= 0 && orient1 >= 0) { return Math.max(orient0, orient1) }
104215             if (orient0 <= 0 && orient1 <= 0) { return Math.max(orient0, orient1) }
104216             return 0
104217           } else if (arguments[0] instanceof Coordinate) {
104218             var p = arguments[0];
104219             return CGAlgorithms.orientationIndex(this.p0, this.p1, p)
104220           }
104221         };
104222         LineSegment.prototype.toGeometry = function toGeometry (geomFactory) {
104223           return geomFactory.createLineString([this.p0, this.p1])
104224         };
104225         LineSegment.prototype.isVertical = function isVertical () {
104226           return this.p0.x === this.p1.x
104227         };
104228         LineSegment.prototype.equals = function equals (o) {
104229           if (!(o instanceof LineSegment)) {
104230             return false
104231           }
104232           var other = o;
104233           return this.p0.equals(other.p0) && this.p1.equals(other.p1)
104234         };
104235         LineSegment.prototype.intersection = function intersection (line) {
104236           var li = new RobustLineIntersector();
104237           li.computeIntersection(this.p0, this.p1, line.p0, line.p1);
104238           if (li.hasIntersection()) { return li.getIntersection(0) }
104239           return null
104240         };
104241         LineSegment.prototype.project = function project () {
104242           if (arguments[0] instanceof Coordinate) {
104243             var p = arguments[0];
104244             if (p.equals(this.p0) || p.equals(this.p1)) { return new Coordinate(p) }
104245             var r = this.projectionFactor(p);
104246             var coord = new Coordinate();
104247             coord.x = this.p0.x + r * (this.p1.x - this.p0.x);
104248             coord.y = this.p0.y + r * (this.p1.y - this.p0.y);
104249             return coord
104250           } else if (arguments[0] instanceof LineSegment) {
104251             var seg = arguments[0];
104252             var pf0 = this.projectionFactor(seg.p0);
104253             var pf1 = this.projectionFactor(seg.p1);
104254             if (pf0 >= 1.0 && pf1 >= 1.0) { return null }
104255             if (pf0 <= 0.0 && pf1 <= 0.0) { return null }
104256             var newp0 = this.project(seg.p0);
104257             if (pf0 < 0.0) { newp0 = this.p0; }
104258             if (pf0 > 1.0) { newp0 = this.p1; }
104259             var newp1 = this.project(seg.p1);
104260             if (pf1 < 0.0) { newp1 = this.p0; }
104261             if (pf1 > 1.0) { newp1 = this.p1; }
104262             return new LineSegment(newp0, newp1)
104263           }
104264         };
104265         LineSegment.prototype.normalize = function normalize () {
104266           if (this.p1.compareTo(this.p0) < 0) { this.reverse(); }
104267         };
104268         LineSegment.prototype.angle = function angle () {
104269           return Math.atan2(this.p1.y - this.p0.y, this.p1.x - this.p0.x)
104270         };
104271         LineSegment.prototype.getCoordinate = function getCoordinate (i) {
104272           if (i === 0) { return this.p0 }
104273           return this.p1
104274         };
104275         LineSegment.prototype.distancePerpendicular = function distancePerpendicular (p) {
104276           return CGAlgorithms.distancePointLinePerpendicular(p, this.p0, this.p1)
104277         };
104278         LineSegment.prototype.minY = function minY () {
104279           return Math.min(this.p0.y, this.p1.y)
104280         };
104281         LineSegment.prototype.midPoint = function midPoint () {
104282           return LineSegment.midPoint(this.p0, this.p1)
104283         };
104284         LineSegment.prototype.projectionFactor = function projectionFactor (p) {
104285           if (p.equals(this.p0)) { return 0.0 }
104286           if (p.equals(this.p1)) { return 1.0 }
104287           var dx = this.p1.x - this.p0.x;
104288           var dy = this.p1.y - this.p0.y;
104289           var len = dx * dx + dy * dy;
104290           if (len <= 0.0) { return Double.NaN }
104291           var r = ((p.x - this.p0.x) * dx + (p.y - this.p0.y) * dy) / len;
104292           return r
104293         };
104294         LineSegment.prototype.closestPoints = function closestPoints (line) {
104295           var intPt = this.intersection(line);
104296           if (intPt !== null) {
104297             return [intPt, intPt]
104298           }
104299           var closestPt = new Array(2).fill(null);
104300           var minDistance = Double.MAX_VALUE;
104301           var dist = null;
104302           var close00 = this.closestPoint(line.p0);
104303           minDistance = close00.distance(line.p0);
104304           closestPt[0] = close00;
104305           closestPt[1] = line.p0;
104306           var close01 = this.closestPoint(line.p1);
104307           dist = close01.distance(line.p1);
104308           if (dist < minDistance) {
104309             minDistance = dist;
104310             closestPt[0] = close01;
104311             closestPt[1] = line.p1;
104312           }
104313           var close10 = line.closestPoint(this.p0);
104314           dist = close10.distance(this.p0);
104315           if (dist < minDistance) {
104316             minDistance = dist;
104317             closestPt[0] = this.p0;
104318             closestPt[1] = close10;
104319           }
104320           var close11 = line.closestPoint(this.p1);
104321           dist = close11.distance(this.p1);
104322           if (dist < minDistance) {
104323             minDistance = dist;
104324             closestPt[0] = this.p1;
104325             closestPt[1] = close11;
104326           }
104327           return closestPt
104328         };
104329         LineSegment.prototype.closestPoint = function closestPoint (p) {
104330           var factor = this.projectionFactor(p);
104331           if (factor > 0 && factor < 1) {
104332             return this.project(p)
104333           }
104334           var dist0 = this.p0.distance(p);
104335           var dist1 = this.p1.distance(p);
104336           if (dist0 < dist1) { return this.p0 }
104337           return this.p1
104338         };
104339         LineSegment.prototype.maxX = function maxX () {
104340           return Math.max(this.p0.x, this.p1.x)
104341         };
104342         LineSegment.prototype.getLength = function getLength () {
104343           return this.p0.distance(this.p1)
104344         };
104345         LineSegment.prototype.compareTo = function compareTo (o) {
104346           var other = o;
104347           var comp0 = this.p0.compareTo(other.p0);
104348           if (comp0 !== 0) { return comp0 }
104349           return this.p1.compareTo(other.p1)
104350         };
104351         LineSegment.prototype.reverse = function reverse () {
104352           var temp = this.p0;
104353           this.p0 = this.p1;
104354           this.p1 = temp;
104355         };
104356         LineSegment.prototype.equalsTopo = function equalsTopo (other) {
104357           return this.p0.equals(other.p0) &&
104358                 (this.p1.equals(other.p1) || this.p0.equals(other.p1)) &&
104359                  this.p1.equals(other.p0)
104360         };
104361         LineSegment.prototype.lineIntersection = function lineIntersection (line) {
104362           try {
104363             var intPt = HCoordinate.intersection(this.p0, this.p1, line.p0, line.p1);
104364             return intPt
104365           } catch (ex) {
104366             if (ex instanceof NotRepresentableException) ; else { throw ex }
104367           } finally {}
104368           return null
104369         };
104370         LineSegment.prototype.maxY = function maxY () {
104371           return Math.max(this.p0.y, this.p1.y)
104372         };
104373         LineSegment.prototype.pointAlongOffset = function pointAlongOffset (segmentLengthFraction, offsetDistance) {
104374           var segx = this.p0.x + segmentLengthFraction * (this.p1.x - this.p0.x);
104375           var segy = this.p0.y + segmentLengthFraction * (this.p1.y - this.p0.y);
104376           var dx = this.p1.x - this.p0.x;
104377           var dy = this.p1.y - this.p0.y;
104378           var len = Math.sqrt(dx * dx + dy * dy);
104379           var ux = 0.0;
104380           var uy = 0.0;
104381           if (offsetDistance !== 0.0) {
104382             if (len <= 0.0) { throw new Error('Cannot compute offset from zero-length line segment') }
104383             ux = offsetDistance * dx / len;
104384             uy = offsetDistance * dy / len;
104385           }
104386           var offsetx = segx - uy;
104387           var offsety = segy + ux;
104388           var coord = new Coordinate(offsetx, offsety);
104389           return coord
104390         };
104391         LineSegment.prototype.setCoordinates = function setCoordinates () {
104392           if (arguments.length === 1) {
104393             var ls = arguments[0];
104394             this.setCoordinates(ls.p0, ls.p1);
104395           } else if (arguments.length === 2) {
104396             var p0 = arguments[0];
104397             var p1 = arguments[1];
104398             this.p0.x = p0.x;
104399             this.p0.y = p0.y;
104400             this.p1.x = p1.x;
104401             this.p1.y = p1.y;
104402           }
104403         };
104404         LineSegment.prototype.segmentFraction = function segmentFraction (inputPt) {
104405           var segFrac = this.projectionFactor(inputPt);
104406           if (segFrac < 0.0) { segFrac = 0.0; } else if (segFrac > 1.0 || Double.isNaN(segFrac)) { segFrac = 1.0; }
104407           return segFrac
104408         };
104409         LineSegment.prototype.toString = function toString () {
104410           return 'LINESTRING( ' + this.p0.x + ' ' + this.p0.y + ', ' + this.p1.x + ' ' + this.p1.y + ')'
104411         };
104412         LineSegment.prototype.isHorizontal = function isHorizontal () {
104413           return this.p0.y === this.p1.y
104414         };
104415         LineSegment.prototype.distance = function distance () {
104416           if (arguments[0] instanceof LineSegment) {
104417             var ls = arguments[0];
104418             return CGAlgorithms.distanceLineLine(this.p0, this.p1, ls.p0, ls.p1)
104419           } else if (arguments[0] instanceof Coordinate) {
104420             var p = arguments[0];
104421             return CGAlgorithms.distancePointLine(p, this.p0, this.p1)
104422           }
104423         };
104424         LineSegment.prototype.pointAlong = function pointAlong (segmentLengthFraction) {
104425           var coord = new Coordinate();
104426           coord.x = this.p0.x + segmentLengthFraction * (this.p1.x - this.p0.x);
104427           coord.y = this.p0.y + segmentLengthFraction * (this.p1.y - this.p0.y);
104428           return coord
104429         };
104430         LineSegment.prototype.hashCode = function hashCode () {
104431           var bits0 = Double.doubleToLongBits(this.p0.x);
104432           bits0 ^= Double.doubleToLongBits(this.p0.y) * 31;
104433           var hash0 = Math.trunc(bits0) ^ Math.trunc(bits0 >> 32);
104434           var bits1 = Double.doubleToLongBits(this.p1.x);
104435           bits1 ^= Double.doubleToLongBits(this.p1.y) * 31;
104436           var hash1 = Math.trunc(bits1) ^ Math.trunc(bits1 >> 32);
104437           return hash0 ^ hash1
104438         };
104439         LineSegment.prototype.interfaces_ = function interfaces_ () {
104440           return [Comparable, Serializable]
104441         };
104442         LineSegment.prototype.getClass = function getClass () {
104443           return LineSegment
104444         };
104445         LineSegment.midPoint = function midPoint (p0, p1) {
104446           return new Coordinate((p0.x + p1.x) / 2, (p0.y + p1.y) / 2)
104447         };
104448         staticAccessors$24.serialVersionUID.get = function () { return 3252005833466256227 };
104449
104450         Object.defineProperties( LineSegment, staticAccessors$24 );
104451
104452         var MonotoneChainOverlapAction = function MonotoneChainOverlapAction () {
104453           this.tempEnv1 = new Envelope();
104454           this.tempEnv2 = new Envelope();
104455           this._overlapSeg1 = new LineSegment();
104456           this._overlapSeg2 = new LineSegment();
104457         };
104458         MonotoneChainOverlapAction.prototype.overlap = function overlap () {
104459           if (arguments.length === 2) ; else if (arguments.length === 4) {
104460             var mc1 = arguments[0];
104461             var start1 = arguments[1];
104462             var mc2 = arguments[2];
104463             var start2 = arguments[3];
104464             mc1.getLineSegment(start1, this._overlapSeg1);
104465             mc2.getLineSegment(start2, this._overlapSeg2);
104466             this.overlap(this._overlapSeg1, this._overlapSeg2);
104467           }
104468         };
104469         MonotoneChainOverlapAction.prototype.interfaces_ = function interfaces_ () {
104470           return []
104471         };
104472         MonotoneChainOverlapAction.prototype.getClass = function getClass () {
104473           return MonotoneChainOverlapAction
104474         };
104475
104476         var MonotoneChain = function MonotoneChain () {
104477           this._pts = null;
104478           this._start = null;
104479           this._end = null;
104480           this._env = null;
104481           this._context = null;
104482           this._id = null;
104483           var pts = arguments[0];
104484           var start = arguments[1];
104485           var end = arguments[2];
104486           var context = arguments[3];
104487           this._pts = pts;
104488           this._start = start;
104489           this._end = end;
104490           this._context = context;
104491         };
104492         MonotoneChain.prototype.getLineSegment = function getLineSegment (index, ls) {
104493           ls.p0 = this._pts[index];
104494           ls.p1 = this._pts[index + 1];
104495         };
104496         MonotoneChain.prototype.computeSelect = function computeSelect (searchEnv, start0, end0, mcs) {
104497           var p0 = this._pts[start0];
104498           var p1 = this._pts[end0];
104499           mcs.tempEnv1.init(p0, p1);
104500           if (end0 - start0 === 1) {
104501             mcs.select(this, start0);
104502             return null
104503           }
104504           if (!searchEnv.intersects(mcs.tempEnv1)) { return null }
104505           var mid = Math.trunc((start0 + end0) / 2);
104506           if (start0 < mid) {
104507             this.computeSelect(searchEnv, start0, mid, mcs);
104508           }
104509           if (mid < end0) {
104510             this.computeSelect(searchEnv, mid, end0, mcs);
104511           }
104512         };
104513         MonotoneChain.prototype.getCoordinates = function getCoordinates () {
104514             var this$1 = this;
104515
104516           var coord = new Array(this._end - this._start + 1).fill(null);
104517           var index = 0;
104518           for (var i = this._start; i <= this._end; i++) {
104519             coord[index++] = this$1._pts[i];
104520           }
104521           return coord
104522         };
104523         MonotoneChain.prototype.computeOverlaps = function computeOverlaps (mc, mco) {
104524           this.computeOverlapsInternal(this._start, this._end, mc, mc._start, mc._end, mco);
104525         };
104526         MonotoneChain.prototype.setId = function setId (id) {
104527           this._id = id;
104528         };
104529         MonotoneChain.prototype.select = function select (searchEnv, mcs) {
104530           this.computeSelect(searchEnv, this._start, this._end, mcs);
104531         };
104532         MonotoneChain.prototype.getEnvelope = function getEnvelope () {
104533           if (this._env === null) {
104534             var p0 = this._pts[this._start];
104535             var p1 = this._pts[this._end];
104536             this._env = new Envelope(p0, p1);
104537           }
104538           return this._env
104539         };
104540         MonotoneChain.prototype.getEndIndex = function getEndIndex () {
104541           return this._end
104542         };
104543         MonotoneChain.prototype.getStartIndex = function getStartIndex () {
104544           return this._start
104545         };
104546         MonotoneChain.prototype.getContext = function getContext () {
104547           return this._context
104548         };
104549         MonotoneChain.prototype.getId = function getId () {
104550           return this._id
104551         };
104552         MonotoneChain.prototype.computeOverlapsInternal = function computeOverlapsInternal (start0, end0, mc, start1, end1, mco) {
104553           var p00 = this._pts[start0];
104554           var p01 = this._pts[end0];
104555           var p10 = mc._pts[start1];
104556           var p11 = mc._pts[end1];
104557           if (end0 - start0 === 1 && end1 - start1 === 1) {
104558             mco.overlap(this, start0, mc, start1);
104559             return null
104560           }
104561           mco.tempEnv1.init(p00, p01);
104562           mco.tempEnv2.init(p10, p11);
104563           if (!mco.tempEnv1.intersects(mco.tempEnv2)) { return null }
104564           var mid0 = Math.trunc((start0 + end0) / 2);
104565           var mid1 = Math.trunc((start1 + end1) / 2);
104566           if (start0 < mid0) {
104567             if (start1 < mid1) { this.computeOverlapsInternal(start0, mid0, mc, start1, mid1, mco); }
104568             if (mid1 < end1) { this.computeOverlapsInternal(start0, mid0, mc, mid1, end1, mco); }
104569           }
104570           if (mid0 < end0) {
104571             if (start1 < mid1) { this.computeOverlapsInternal(mid0, end0, mc, start1, mid1, mco); }
104572             if (mid1 < end1) { this.computeOverlapsInternal(mid0, end0, mc, mid1, end1, mco); }
104573           }
104574         };
104575         MonotoneChain.prototype.interfaces_ = function interfaces_ () {
104576           return []
104577         };
104578         MonotoneChain.prototype.getClass = function getClass () {
104579           return MonotoneChain
104580         };
104581
104582         var MonotoneChainBuilder = function MonotoneChainBuilder () {};
104583
104584         MonotoneChainBuilder.prototype.interfaces_ = function interfaces_ () {
104585           return []
104586         };
104587         MonotoneChainBuilder.prototype.getClass = function getClass () {
104588           return MonotoneChainBuilder
104589         };
104590         MonotoneChainBuilder.getChainStartIndices = function getChainStartIndices (pts) {
104591           var start = 0;
104592           var startIndexList = new ArrayList();
104593           startIndexList.add(new Integer(start));
104594           do {
104595             var last = MonotoneChainBuilder.findChainEnd(pts, start);
104596             startIndexList.add(new Integer(last));
104597             start = last;
104598           } while (start < pts.length - 1)
104599           var startIndex = MonotoneChainBuilder.toIntArray(startIndexList);
104600           return startIndex
104601         };
104602         MonotoneChainBuilder.findChainEnd = function findChainEnd (pts, start) {
104603           var safeStart = start;
104604           while (safeStart < pts.length - 1 && pts[safeStart].equals2D(pts[safeStart + 1])) {
104605             safeStart++;
104606           }
104607           if (safeStart >= pts.length - 1) {
104608             return pts.length - 1
104609           }
104610           var chainQuad = Quadrant.quadrant(pts[safeStart], pts[safeStart + 1]);
104611           var last = start + 1;
104612           while (last < pts.length) {
104613             if (!pts[last - 1].equals2D(pts[last])) {
104614               var quad = Quadrant.quadrant(pts[last - 1], pts[last]);
104615               if (quad !== chainQuad) { break }
104616             }
104617             last++;
104618           }
104619           return last - 1
104620         };
104621         MonotoneChainBuilder.getChains = function getChains () {
104622           if (arguments.length === 1) {
104623             var pts = arguments[0];
104624             return MonotoneChainBuilder.getChains(pts, null)
104625           } else if (arguments.length === 2) {
104626             var pts$1 = arguments[0];
104627             var context = arguments[1];
104628             var mcList = new ArrayList();
104629             var startIndex = MonotoneChainBuilder.getChainStartIndices(pts$1);
104630             for (var i = 0; i < startIndex.length - 1; i++) {
104631               var mc = new MonotoneChain(pts$1, startIndex[i], startIndex[i + 1], context);
104632               mcList.add(mc);
104633             }
104634             return mcList
104635           }
104636         };
104637         MonotoneChainBuilder.toIntArray = function toIntArray (list) {
104638           var array = new Array(list.size()).fill(null);
104639           for (var i = 0; i < array.length; i++) {
104640             array[i] = list.get(i).intValue();
104641           }
104642           return array
104643         };
104644
104645         var Noder = function Noder () {};
104646
104647         Noder.prototype.computeNodes = function computeNodes (segStrings) {};
104648         Noder.prototype.getNodedSubstrings = function getNodedSubstrings () {};
104649         Noder.prototype.interfaces_ = function interfaces_ () {
104650           return []
104651         };
104652         Noder.prototype.getClass = function getClass () {
104653           return Noder
104654         };
104655
104656         var SinglePassNoder = function SinglePassNoder () {
104657           this._segInt = null;
104658           if (arguments.length === 0) ; else if (arguments.length === 1) {
104659             var segInt = arguments[0];
104660             this.setSegmentIntersector(segInt);
104661           }
104662         };
104663         SinglePassNoder.prototype.setSegmentIntersector = function setSegmentIntersector (segInt) {
104664           this._segInt = segInt;
104665         };
104666         SinglePassNoder.prototype.interfaces_ = function interfaces_ () {
104667           return [Noder]
104668         };
104669         SinglePassNoder.prototype.getClass = function getClass () {
104670           return SinglePassNoder
104671         };
104672
104673         var MCIndexNoder = (function (SinglePassNoder$$1) {
104674           function MCIndexNoder (si) {
104675             if (si) { SinglePassNoder$$1.call(this, si); }
104676             else { SinglePassNoder$$1.call(this); }
104677             this._monoChains = new ArrayList();
104678             this._index = new STRtree();
104679             this._idCounter = 0;
104680             this._nodedSegStrings = null;
104681             this._nOverlaps = 0;
104682           }
104683
104684           if ( SinglePassNoder$$1 ) { MCIndexNoder.__proto__ = SinglePassNoder$$1; }
104685           MCIndexNoder.prototype = Object.create( SinglePassNoder$$1 && SinglePassNoder$$1.prototype );
104686           MCIndexNoder.prototype.constructor = MCIndexNoder;
104687
104688           var staticAccessors = { SegmentOverlapAction: { configurable: true } };
104689           MCIndexNoder.prototype.getMonotoneChains = function getMonotoneChains () {
104690             return this._monoChains
104691           };
104692           MCIndexNoder.prototype.getNodedSubstrings = function getNodedSubstrings () {
104693             return NodedSegmentString.getNodedSubstrings(this._nodedSegStrings)
104694           };
104695           MCIndexNoder.prototype.getIndex = function getIndex () {
104696             return this._index
104697           };
104698           MCIndexNoder.prototype.add = function add (segStr) {
104699             var this$1 = this;
104700
104701             var segChains = MonotoneChainBuilder.getChains(segStr.getCoordinates(), segStr);
104702             for (var i = segChains.iterator(); i.hasNext();) {
104703               var mc = i.next();
104704               mc.setId(this$1._idCounter++);
104705               this$1._index.insert(mc.getEnvelope(), mc);
104706               this$1._monoChains.add(mc);
104707             }
104708           };
104709           MCIndexNoder.prototype.computeNodes = function computeNodes (inputSegStrings) {
104710             var this$1 = this;
104711
104712             this._nodedSegStrings = inputSegStrings;
104713             for (var i = inputSegStrings.iterator(); i.hasNext();) {
104714               this$1.add(i.next());
104715             }
104716             this.intersectChains();
104717           };
104718           MCIndexNoder.prototype.intersectChains = function intersectChains () {
104719             var this$1 = this;
104720
104721             var overlapAction = new SegmentOverlapAction(this._segInt);
104722             for (var i = this._monoChains.iterator(); i.hasNext();) {
104723               var queryChain = i.next();
104724               var overlapChains = this$1._index.query(queryChain.getEnvelope());
104725               for (var j = overlapChains.iterator(); j.hasNext();) {
104726                 var testChain = j.next();
104727                 if (testChain.getId() > queryChain.getId()) {
104728                   queryChain.computeOverlaps(testChain, overlapAction);
104729                   this$1._nOverlaps++;
104730                 }
104731                 if (this$1._segInt.isDone()) { return null }
104732               }
104733             }
104734           };
104735           MCIndexNoder.prototype.interfaces_ = function interfaces_ () {
104736             return []
104737           };
104738           MCIndexNoder.prototype.getClass = function getClass () {
104739             return MCIndexNoder
104740           };
104741           staticAccessors.SegmentOverlapAction.get = function () { return SegmentOverlapAction };
104742
104743           Object.defineProperties( MCIndexNoder, staticAccessors );
104744
104745           return MCIndexNoder;
104746         }(SinglePassNoder));
104747
104748         var SegmentOverlapAction = (function (MonotoneChainOverlapAction$$1) {
104749           function SegmentOverlapAction () {
104750             MonotoneChainOverlapAction$$1.call(this);
104751             this._si = null;
104752             var si = arguments[0];
104753             this._si = si;
104754           }
104755
104756           if ( MonotoneChainOverlapAction$$1 ) { SegmentOverlapAction.__proto__ = MonotoneChainOverlapAction$$1; }
104757           SegmentOverlapAction.prototype = Object.create( MonotoneChainOverlapAction$$1 && MonotoneChainOverlapAction$$1.prototype );
104758           SegmentOverlapAction.prototype.constructor = SegmentOverlapAction;
104759           SegmentOverlapAction.prototype.overlap = function overlap () {
104760             if (arguments.length === 4) {
104761               var mc1 = arguments[0];
104762               var start1 = arguments[1];
104763               var mc2 = arguments[2];
104764               var start2 = arguments[3];
104765               var ss1 = mc1.getContext();
104766               var ss2 = mc2.getContext();
104767               this._si.processIntersections(ss1, start1, ss2, start2);
104768             } else { return MonotoneChainOverlapAction$$1.prototype.overlap.apply(this, arguments) }
104769           };
104770           SegmentOverlapAction.prototype.interfaces_ = function interfaces_ () {
104771             return []
104772           };
104773           SegmentOverlapAction.prototype.getClass = function getClass () {
104774             return SegmentOverlapAction
104775           };
104776
104777           return SegmentOverlapAction;
104778         }(MonotoneChainOverlapAction));
104779
104780         var BufferParameters = function BufferParameters () {
104781           this._quadrantSegments = BufferParameters.DEFAULT_QUADRANT_SEGMENTS;
104782           this._endCapStyle = BufferParameters.CAP_ROUND;
104783           this._joinStyle = BufferParameters.JOIN_ROUND;
104784           this._mitreLimit = BufferParameters.DEFAULT_MITRE_LIMIT;
104785           this._isSingleSided = false;
104786           this._simplifyFactor = BufferParameters.DEFAULT_SIMPLIFY_FACTOR;
104787
104788           if (arguments.length === 0) ; else if (arguments.length === 1) {
104789             var quadrantSegments = arguments[0];
104790             this.setQuadrantSegments(quadrantSegments);
104791           } else if (arguments.length === 2) {
104792             var quadrantSegments$1 = arguments[0];
104793             var endCapStyle = arguments[1];
104794             this.setQuadrantSegments(quadrantSegments$1);
104795             this.setEndCapStyle(endCapStyle);
104796           } else if (arguments.length === 4) {
104797             var quadrantSegments$2 = arguments[0];
104798             var endCapStyle$1 = arguments[1];
104799             var joinStyle = arguments[2];
104800             var mitreLimit = arguments[3];
104801             this.setQuadrantSegments(quadrantSegments$2);
104802             this.setEndCapStyle(endCapStyle$1);
104803             this.setJoinStyle(joinStyle);
104804             this.setMitreLimit(mitreLimit);
104805           }
104806         };
104807
104808         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 } };
104809         BufferParameters.prototype.getEndCapStyle = function getEndCapStyle () {
104810           return this._endCapStyle
104811         };
104812         BufferParameters.prototype.isSingleSided = function isSingleSided () {
104813           return this._isSingleSided
104814         };
104815         BufferParameters.prototype.setQuadrantSegments = function setQuadrantSegments (quadSegs) {
104816           this._quadrantSegments = quadSegs;
104817           if (this._quadrantSegments === 0) { this._joinStyle = BufferParameters.JOIN_BEVEL; }
104818           if (this._quadrantSegments < 0) {
104819             this._joinStyle = BufferParameters.JOIN_MITRE;
104820             this._mitreLimit = Math.abs(this._quadrantSegments);
104821           }
104822           if (quadSegs <= 0) {
104823             this._quadrantSegments = 1;
104824           }
104825           if (this._joinStyle !== BufferParameters.JOIN_ROUND) {
104826             this._quadrantSegments = BufferParameters.DEFAULT_QUADRANT_SEGMENTS;
104827           }
104828         };
104829         BufferParameters.prototype.getJoinStyle = function getJoinStyle () {
104830           return this._joinStyle
104831         };
104832         BufferParameters.prototype.setJoinStyle = function setJoinStyle (joinStyle) {
104833           this._joinStyle = joinStyle;
104834         };
104835         BufferParameters.prototype.setSimplifyFactor = function setSimplifyFactor (simplifyFactor) {
104836           this._simplifyFactor = simplifyFactor < 0 ? 0 : simplifyFactor;
104837         };
104838         BufferParameters.prototype.getSimplifyFactor = function getSimplifyFactor () {
104839           return this._simplifyFactor
104840         };
104841         BufferParameters.prototype.getQuadrantSegments = function getQuadrantSegments () {
104842           return this._quadrantSegments
104843         };
104844         BufferParameters.prototype.setEndCapStyle = function setEndCapStyle (endCapStyle) {
104845           this._endCapStyle = endCapStyle;
104846         };
104847         BufferParameters.prototype.getMitreLimit = function getMitreLimit () {
104848           return this._mitreLimit
104849         };
104850         BufferParameters.prototype.setMitreLimit = function setMitreLimit (mitreLimit) {
104851           this._mitreLimit = mitreLimit;
104852         };
104853         BufferParameters.prototype.setSingleSided = function setSingleSided (isSingleSided) {
104854           this._isSingleSided = isSingleSided;
104855         };
104856         BufferParameters.prototype.interfaces_ = function interfaces_ () {
104857           return []
104858         };
104859         BufferParameters.prototype.getClass = function getClass () {
104860           return BufferParameters
104861         };
104862         BufferParameters.bufferDistanceError = function bufferDistanceError (quadSegs) {
104863           var alpha = Math.PI / 2.0 / quadSegs;
104864           return 1 - Math.cos(alpha / 2.0)
104865         };
104866         staticAccessors$25.CAP_ROUND.get = function () { return 1 };
104867         staticAccessors$25.CAP_FLAT.get = function () { return 2 };
104868         staticAccessors$25.CAP_SQUARE.get = function () { return 3 };
104869         staticAccessors$25.JOIN_ROUND.get = function () { return 1 };
104870         staticAccessors$25.JOIN_MITRE.get = function () { return 2 };
104871         staticAccessors$25.JOIN_BEVEL.get = function () { return 3 };
104872         staticAccessors$25.DEFAULT_QUADRANT_SEGMENTS.get = function () { return 8 };
104873         staticAccessors$25.DEFAULT_MITRE_LIMIT.get = function () { return 5.0 };
104874         staticAccessors$25.DEFAULT_SIMPLIFY_FACTOR.get = function () { return 0.01 };
104875
104876         Object.defineProperties( BufferParameters, staticAccessors$25 );
104877
104878         var BufferInputLineSimplifier = function BufferInputLineSimplifier (inputLine) {
104879           this._distanceTol = null;
104880           this._isDeleted = null;
104881           this._angleOrientation = CGAlgorithms.COUNTERCLOCKWISE;
104882           this._inputLine = inputLine || null;
104883         };
104884
104885         var staticAccessors$26 = { INIT: { configurable: true },DELETE: { configurable: true },KEEP: { configurable: true },NUM_PTS_TO_CHECK: { configurable: true } };
104886         BufferInputLineSimplifier.prototype.isDeletable = function isDeletable (i0, i1, i2, distanceTol) {
104887           var p0 = this._inputLine[i0];
104888           var p1 = this._inputLine[i1];
104889           var p2 = this._inputLine[i2];
104890           if (!this.isConcave(p0, p1, p2)) { return false }
104891           if (!this.isShallow(p0, p1, p2, distanceTol)) { return false }
104892           return this.isShallowSampled(p0, p1, i0, i2, distanceTol)
104893         };
104894         BufferInputLineSimplifier.prototype.deleteShallowConcavities = function deleteShallowConcavities () {
104895             var this$1 = this;
104896
104897           var index = 1;
104898           // const maxIndex = this._inputLine.length - 1
104899           var midIndex = this.findNextNonDeletedIndex(index);
104900           var lastIndex = this.findNextNonDeletedIndex(midIndex);
104901           var isChanged = false;
104902           while (lastIndex < this._inputLine.length) {
104903             var isMiddleVertexDeleted = false;
104904             if (this$1.isDeletable(index, midIndex, lastIndex, this$1._distanceTol)) {
104905               this$1._isDeleted[midIndex] = BufferInputLineSimplifier.DELETE;
104906               isMiddleVertexDeleted = true;
104907               isChanged = true;
104908             }
104909             if (isMiddleVertexDeleted) { index = lastIndex; } else { index = midIndex; }
104910             midIndex = this$1.findNextNonDeletedIndex(index);
104911             lastIndex = this$1.findNextNonDeletedIndex(midIndex);
104912           }
104913           return isChanged
104914         };
104915         BufferInputLineSimplifier.prototype.isShallowConcavity = function isShallowConcavity (p0, p1, p2, distanceTol) {
104916           var orientation = CGAlgorithms.computeOrientation(p0, p1, p2);
104917           var isAngleToSimplify = orientation === this._angleOrientation;
104918           if (!isAngleToSimplify) { return false }
104919           var dist = CGAlgorithms.distancePointLine(p1, p0, p2);
104920           return dist < distanceTol
104921         };
104922         BufferInputLineSimplifier.prototype.isShallowSampled = function isShallowSampled (p0, p2, i0, i2, distanceTol) {
104923             var this$1 = this;
104924
104925           var inc = Math.trunc((i2 - i0) / BufferInputLineSimplifier.NUM_PTS_TO_CHECK);
104926           if (inc <= 0) { inc = 1; }
104927           for (var i = i0; i < i2; i += inc) {
104928             if (!this$1.isShallow(p0, p2, this$1._inputLine[i], distanceTol)) { return false }
104929           }
104930           return true
104931         };
104932         BufferInputLineSimplifier.prototype.isConcave = function isConcave (p0, p1, p2) {
104933           var orientation = CGAlgorithms.computeOrientation(p0, p1, p2);
104934           var isConcave = orientation === this._angleOrientation;
104935           return isConcave
104936         };
104937         BufferInputLineSimplifier.prototype.simplify = function simplify (distanceTol) {
104938             var this$1 = this;
104939
104940           this._distanceTol = Math.abs(distanceTol);
104941           if (distanceTol < 0) { this._angleOrientation = CGAlgorithms.CLOCKWISE; }
104942           this._isDeleted = new Array(this._inputLine.length).fill(null);
104943           var isChanged = false;
104944           do {
104945             isChanged = this$1.deleteShallowConcavities();
104946           } while (isChanged)
104947           return this.collapseLine()
104948         };
104949         BufferInputLineSimplifier.prototype.findNextNonDeletedIndex = function findNextNonDeletedIndex (index) {
104950           var next = index + 1;
104951           while (next < this._inputLine.length && this._isDeleted[next] === BufferInputLineSimplifier.DELETE) { next++; }
104952           return next
104953         };
104954         BufferInputLineSimplifier.prototype.isShallow = function isShallow (p0, p1, p2, distanceTol) {
104955           var dist = CGAlgorithms.distancePointLine(p1, p0, p2);
104956           return dist < distanceTol
104957         };
104958         BufferInputLineSimplifier.prototype.collapseLine = function collapseLine () {
104959             var this$1 = this;
104960
104961           var coordList = new CoordinateList();
104962           for (var i = 0; i < this._inputLine.length; i++) {
104963             if (this$1._isDeleted[i] !== BufferInputLineSimplifier.DELETE) { coordList.add(this$1._inputLine[i]); }
104964           }
104965           return coordList.toCoordinateArray()
104966         };
104967         BufferInputLineSimplifier.prototype.interfaces_ = function interfaces_ () {
104968           return []
104969         };
104970         BufferInputLineSimplifier.prototype.getClass = function getClass () {
104971           return BufferInputLineSimplifier
104972         };
104973         BufferInputLineSimplifier.simplify = function simplify (inputLine, distanceTol) {
104974           var simp = new BufferInputLineSimplifier(inputLine);
104975           return simp.simplify(distanceTol)
104976         };
104977         staticAccessors$26.INIT.get = function () { return 0 };
104978         staticAccessors$26.DELETE.get = function () { return 1 };
104979         staticAccessors$26.KEEP.get = function () { return 1 };
104980         staticAccessors$26.NUM_PTS_TO_CHECK.get = function () { return 10 };
104981
104982         Object.defineProperties( BufferInputLineSimplifier, staticAccessors$26 );
104983
104984         var OffsetSegmentString = function OffsetSegmentString () {
104985           this._ptList = null;
104986           this._precisionModel = null;
104987           this._minimimVertexDistance = 0.0;
104988           this._ptList = new ArrayList();
104989         };
104990
104991         var staticAccessors$28 = { COORDINATE_ARRAY_TYPE: { configurable: true } };
104992         OffsetSegmentString.prototype.getCoordinates = function getCoordinates () {
104993           var coord = this._ptList.toArray(OffsetSegmentString.COORDINATE_ARRAY_TYPE);
104994           return coord
104995         };
104996         OffsetSegmentString.prototype.setPrecisionModel = function setPrecisionModel (precisionModel) {
104997           this._precisionModel = precisionModel;
104998         };
104999         OffsetSegmentString.prototype.addPt = function addPt (pt) {
105000           var bufPt = new Coordinate(pt);
105001           this._precisionModel.makePrecise(bufPt);
105002           if (this.isRedundant(bufPt)) { return null }
105003           this._ptList.add(bufPt);
105004         };
105005         OffsetSegmentString.prototype.revere = function revere () {};
105006         OffsetSegmentString.prototype.addPts = function addPts (pt, isForward) {
105007             var this$1 = this;
105008
105009           if (isForward) {
105010             for (var i = 0; i < pt.length; i++) {
105011               this$1.addPt(pt[i]);
105012             }
105013           } else {
105014             for (var i$1 = pt.length - 1; i$1 >= 0; i$1--) {
105015               this$1.addPt(pt[i$1]);
105016             }
105017           }
105018         };
105019         OffsetSegmentString.prototype.isRedundant = function isRedundant (pt) {
105020           if (this._ptList.size() < 1) { return false }
105021           var lastPt = this._ptList.get(this._ptList.size() - 1);
105022           var ptDist = pt.distance(lastPt);
105023           if (ptDist < this._minimimVertexDistance) { return true }
105024           return false
105025         };
105026         OffsetSegmentString.prototype.toString = function toString () {
105027           var fact = new GeometryFactory();
105028           var line = fact.createLineString(this.getCoordinates());
105029           return line.toString()
105030         };
105031         OffsetSegmentString.prototype.closeRing = function closeRing () {
105032           if (this._ptList.size() < 1) { return null }
105033           var startPt = new Coordinate(this._ptList.get(0));
105034           var lastPt = this._ptList.get(this._ptList.size() - 1);
105035           // const last2Pt = null
105036           // if (this._ptList.size() >= 2) last2Pt = this._ptList.get(this._ptList.size() - 2)
105037           if (startPt.equals(lastPt)) { return null }
105038           this._ptList.add(startPt);
105039         };
105040         OffsetSegmentString.prototype.setMinimumVertexDistance = function setMinimumVertexDistance (minimimVertexDistance) {
105041           this._minimimVertexDistance = minimimVertexDistance;
105042         };
105043         OffsetSegmentString.prototype.interfaces_ = function interfaces_ () {
105044           return []
105045         };
105046         OffsetSegmentString.prototype.getClass = function getClass () {
105047           return OffsetSegmentString
105048         };
105049         staticAccessors$28.COORDINATE_ARRAY_TYPE.get = function () { return new Array(0).fill(null) };
105050
105051         Object.defineProperties( OffsetSegmentString, staticAccessors$28 );
105052
105053         var Angle = function Angle () {};
105054
105055         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 } };
105056
105057         Angle.prototype.interfaces_ = function interfaces_ () {
105058           return []
105059         };
105060         Angle.prototype.getClass = function getClass () {
105061           return Angle
105062         };
105063         Angle.toDegrees = function toDegrees (radians) {
105064           return radians * 180 / Math.PI
105065         };
105066         Angle.normalize = function normalize (angle) {
105067           while (angle > Math.PI) { angle -= Angle.PI_TIMES_2; }
105068           while (angle <= -Math.PI) { angle += Angle.PI_TIMES_2; }
105069           return angle
105070         };
105071         Angle.angle = function angle () {
105072           if (arguments.length === 1) {
105073             var p = arguments[0];
105074             return Math.atan2(p.y, p.x)
105075           } else if (arguments.length === 2) {
105076             var p0 = arguments[0];
105077             var p1 = arguments[1];
105078             var dx = p1.x - p0.x;
105079             var dy = p1.y - p0.y;
105080             return Math.atan2(dy, dx)
105081           }
105082         };
105083         Angle.isAcute = function isAcute (p0, p1, p2) {
105084           var dx0 = p0.x - p1.x;
105085           var dy0 = p0.y - p1.y;
105086           var dx1 = p2.x - p1.x;
105087           var dy1 = p2.y - p1.y;
105088           var dotprod = dx0 * dx1 + dy0 * dy1;
105089           return dotprod > 0
105090         };
105091         Angle.isObtuse = function isObtuse (p0, p1, p2) {
105092           var dx0 = p0.x - p1.x;
105093           var dy0 = p0.y - p1.y;
105094           var dx1 = p2.x - p1.x;
105095           var dy1 = p2.y - p1.y;
105096           var dotprod = dx0 * dx1 + dy0 * dy1;
105097           return dotprod < 0
105098         };
105099         Angle.interiorAngle = function interiorAngle (p0, p1, p2) {
105100           var anglePrev = Angle.angle(p1, p0);
105101           var angleNext = Angle.angle(p1, p2);
105102           return Math.abs(angleNext - anglePrev)
105103         };
105104         Angle.normalizePositive = function normalizePositive (angle) {
105105           if (angle < 0.0) {
105106             while (angle < 0.0) { angle += Angle.PI_TIMES_2; }
105107             if (angle >= Angle.PI_TIMES_2) { angle = 0.0; }
105108           } else {
105109             while (angle >= Angle.PI_TIMES_2) { angle -= Angle.PI_TIMES_2; }
105110             if (angle < 0.0) { angle = 0.0; }
105111           }
105112           return angle
105113         };
105114         Angle.angleBetween = function angleBetween (tip1, tail, tip2) {
105115           var a1 = Angle.angle(tail, tip1);
105116           var a2 = Angle.angle(tail, tip2);
105117           return Angle.diff(a1, a2)
105118         };
105119         Angle.diff = function diff (ang1, ang2) {
105120           var delAngle = null;
105121           if (ang1 < ang2) {
105122             delAngle = ang2 - ang1;
105123           } else {
105124             delAngle = ang1 - ang2;
105125           }
105126           if (delAngle > Math.PI) {
105127             delAngle = 2 * Math.PI - delAngle;
105128           }
105129           return delAngle
105130         };
105131         Angle.toRadians = function toRadians (angleDegrees) {
105132           return angleDegrees * Math.PI / 180.0
105133         };
105134         Angle.getTurn = function getTurn (ang1, ang2) {
105135           var crossproduct = Math.sin(ang2 - ang1);
105136           if (crossproduct > 0) {
105137             return Angle.COUNTERCLOCKWISE
105138           }
105139           if (crossproduct < 0) {
105140             return Angle.CLOCKWISE
105141           }
105142           return Angle.NONE
105143         };
105144         Angle.angleBetweenOriented = function angleBetweenOriented (tip1, tail, tip2) {
105145           var a1 = Angle.angle(tail, tip1);
105146           var a2 = Angle.angle(tail, tip2);
105147           var angDel = a2 - a1;
105148           if (angDel <= -Math.PI) { return angDel + Angle.PI_TIMES_2 }
105149           if (angDel > Math.PI) { return angDel - Angle.PI_TIMES_2 }
105150           return angDel
105151         };
105152         staticAccessors$29.PI_TIMES_2.get = function () { return 2.0 * Math.PI };
105153         staticAccessors$29.PI_OVER_2.get = function () { return Math.PI / 2.0 };
105154         staticAccessors$29.PI_OVER_4.get = function () { return Math.PI / 4.0 };
105155         staticAccessors$29.COUNTERCLOCKWISE.get = function () { return CGAlgorithms.COUNTERCLOCKWISE };
105156         staticAccessors$29.CLOCKWISE.get = function () { return CGAlgorithms.CLOCKWISE };
105157         staticAccessors$29.NONE.get = function () { return CGAlgorithms.COLLINEAR };
105158
105159         Object.defineProperties( Angle, staticAccessors$29 );
105160
105161         var OffsetSegmentGenerator = function OffsetSegmentGenerator () {
105162           this._maxCurveSegmentError = 0.0;
105163           this._filletAngleQuantum = null;
105164           this._closingSegLengthFactor = 1;
105165           this._segList = null;
105166           this._distance = 0.0;
105167           this._precisionModel = null;
105168           this._bufParams = null;
105169           this._li = null;
105170           this._s0 = null;
105171           this._s1 = null;
105172           this._s2 = null;
105173           this._seg0 = new LineSegment();
105174           this._seg1 = new LineSegment();
105175           this._offset0 = new LineSegment();
105176           this._offset1 = new LineSegment();
105177           this._side = 0;
105178           this._hasNarrowConcaveAngle = false;
105179           var precisionModel = arguments[0];
105180           var bufParams = arguments[1];
105181           var distance = arguments[2];
105182           this._precisionModel = precisionModel;
105183           this._bufParams = bufParams;
105184           this._li = new RobustLineIntersector();
105185           this._filletAngleQuantum = Math.PI / 2.0 / bufParams.getQuadrantSegments();
105186           if (bufParams.getQuadrantSegments() >= 8 && bufParams.getJoinStyle() === BufferParameters.JOIN_ROUND) { this._closingSegLengthFactor = OffsetSegmentGenerator.MAX_CLOSING_SEG_LEN_FACTOR; }
105187           this.init(distance);
105188         };
105189
105190         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 } };
105191         OffsetSegmentGenerator.prototype.addNextSegment = function addNextSegment (p, addStartPoint) {
105192           this._s0 = this._s1;
105193           this._s1 = this._s2;
105194           this._s2 = p;
105195           this._seg0.setCoordinates(this._s0, this._s1);
105196           this.computeOffsetSegment(this._seg0, this._side, this._distance, this._offset0);
105197           this._seg1.setCoordinates(this._s1, this._s2);
105198           this.computeOffsetSegment(this._seg1, this._side, this._distance, this._offset1);
105199           if (this._s1.equals(this._s2)) { return null }
105200           var orientation = CGAlgorithms.computeOrientation(this._s0, this._s1, this._s2);
105201           var outsideTurn = (orientation === CGAlgorithms.CLOCKWISE && this._side === Position.LEFT) || (orientation === CGAlgorithms.COUNTERCLOCKWISE && this._side === Position.RIGHT);
105202           if (orientation === 0) {
105203             this.addCollinear(addStartPoint);
105204           } else if (outsideTurn) {
105205             this.addOutsideTurn(orientation, addStartPoint);
105206           } else {
105207             this.addInsideTurn(orientation, addStartPoint);
105208           }
105209         };
105210         OffsetSegmentGenerator.prototype.addLineEndCap = function addLineEndCap (p0, p1) {
105211           var seg = new LineSegment(p0, p1);
105212           var offsetL = new LineSegment();
105213           this.computeOffsetSegment(seg, Position.LEFT, this._distance, offsetL);
105214           var offsetR = new LineSegment();
105215           this.computeOffsetSegment(seg, Position.RIGHT, this._distance, offsetR);
105216           var dx = p1.x - p0.x;
105217           var dy = p1.y - p0.y;
105218           var angle = Math.atan2(dy, dx);
105219           switch (this._bufParams.getEndCapStyle()) {
105220             case BufferParameters.CAP_ROUND:
105221               this._segList.addPt(offsetL.p1);
105222               this.addFilletArc(p1, angle + Math.PI / 2, angle - Math.PI / 2, CGAlgorithms.CLOCKWISE, this._distance);
105223               this._segList.addPt(offsetR.p1);
105224               break
105225             case BufferParameters.CAP_FLAT:
105226               this._segList.addPt(offsetL.p1);
105227               this._segList.addPt(offsetR.p1);
105228               break
105229             case BufferParameters.CAP_SQUARE:
105230               var squareCapSideOffset = new Coordinate();
105231               squareCapSideOffset.x = Math.abs(this._distance) * Math.cos(angle);
105232               squareCapSideOffset.y = Math.abs(this._distance) * Math.sin(angle);
105233               var squareCapLOffset = new Coordinate(offsetL.p1.x + squareCapSideOffset.x, offsetL.p1.y + squareCapSideOffset.y);
105234               var squareCapROffset = new Coordinate(offsetR.p1.x + squareCapSideOffset.x, offsetR.p1.y + squareCapSideOffset.y);
105235               this._segList.addPt(squareCapLOffset);
105236               this._segList.addPt(squareCapROffset);
105237               break
105238           }
105239         };
105240         OffsetSegmentGenerator.prototype.getCoordinates = function getCoordinates () {
105241           var pts = this._segList.getCoordinates();
105242           return pts
105243         };
105244         OffsetSegmentGenerator.prototype.addMitreJoin = function addMitreJoin (p, offset0, offset1, distance) {
105245           var isMitreWithinLimit = true;
105246           var intPt = null;
105247           try {
105248             intPt = HCoordinate.intersection(offset0.p0, offset0.p1, offset1.p0, offset1.p1);
105249             var mitreRatio = distance <= 0.0 ? 1.0 : intPt.distance(p) / Math.abs(distance);
105250             if (mitreRatio > this._bufParams.getMitreLimit()) { isMitreWithinLimit = false; }
105251           } catch (ex) {
105252             if (ex instanceof NotRepresentableException) {
105253               intPt = new Coordinate(0, 0);
105254               isMitreWithinLimit = false;
105255             } else { throw ex }
105256           } finally {}
105257           if (isMitreWithinLimit) {
105258             this._segList.addPt(intPt);
105259           } else {
105260             this.addLimitedMitreJoin(offset0, offset1, distance, this._bufParams.getMitreLimit());
105261           }
105262         };
105263         OffsetSegmentGenerator.prototype.addFilletCorner = function addFilletCorner (p, p0, p1, direction, radius) {
105264           var dx0 = p0.x - p.x;
105265           var dy0 = p0.y - p.y;
105266           var startAngle = Math.atan2(dy0, dx0);
105267           var dx1 = p1.x - p.x;
105268           var dy1 = p1.y - p.y;
105269           var endAngle = Math.atan2(dy1, dx1);
105270           if (direction === CGAlgorithms.CLOCKWISE) {
105271             if (startAngle <= endAngle) { startAngle += 2.0 * Math.PI; }
105272           } else {
105273             if (startAngle >= endAngle) { startAngle -= 2.0 * Math.PI; }
105274           }
105275           this._segList.addPt(p0);
105276           this.addFilletArc(p, startAngle, endAngle, direction, radius);
105277           this._segList.addPt(p1);
105278         };
105279         OffsetSegmentGenerator.prototype.addOutsideTurn = function addOutsideTurn (orientation, addStartPoint) {
105280           if (this._offset0.p1.distance(this._offset1.p0) < this._distance * OffsetSegmentGenerator.OFFSET_SEGMENT_SEPARATION_FACTOR) {
105281             this._segList.addPt(this._offset0.p1);
105282             return null
105283           }
105284           if (this._bufParams.getJoinStyle() === BufferParameters.JOIN_MITRE) {
105285             this.addMitreJoin(this._s1, this._offset0, this._offset1, this._distance);
105286           } else if (this._bufParams.getJoinStyle() === BufferParameters.JOIN_BEVEL) {
105287             this.addBevelJoin(this._offset0, this._offset1);
105288           } else {
105289             if (addStartPoint) { this._segList.addPt(this._offset0.p1); }
105290             this.addFilletCorner(this._s1, this._offset0.p1, this._offset1.p0, orientation, this._distance);
105291             this._segList.addPt(this._offset1.p0);
105292           }
105293         };
105294         OffsetSegmentGenerator.prototype.createSquare = function createSquare (p) {
105295           this._segList.addPt(new Coordinate(p.x + this._distance, p.y + this._distance));
105296           this._segList.addPt(new Coordinate(p.x + this._distance, p.y - this._distance));
105297           this._segList.addPt(new Coordinate(p.x - this._distance, p.y - this._distance));
105298           this._segList.addPt(new Coordinate(p.x - this._distance, p.y + this._distance));
105299           this._segList.closeRing();
105300         };
105301         OffsetSegmentGenerator.prototype.addSegments = function addSegments (pt, isForward) {
105302           this._segList.addPts(pt, isForward);
105303         };
105304         OffsetSegmentGenerator.prototype.addFirstSegment = function addFirstSegment () {
105305           this._segList.addPt(this._offset1.p0);
105306         };
105307         OffsetSegmentGenerator.prototype.addLastSegment = function addLastSegment () {
105308           this._segList.addPt(this._offset1.p1);
105309         };
105310         OffsetSegmentGenerator.prototype.initSideSegments = function initSideSegments (s1, s2, side) {
105311           this._s1 = s1;
105312           this._s2 = s2;
105313           this._side = side;
105314           this._seg1.setCoordinates(s1, s2);
105315           this.computeOffsetSegment(this._seg1, side, this._distance, this._offset1);
105316         };
105317         OffsetSegmentGenerator.prototype.addLimitedMitreJoin = function addLimitedMitreJoin (offset0, offset1, distance, mitreLimit) {
105318           var basePt = this._seg0.p1;
105319           var ang0 = Angle.angle(basePt, this._seg0.p0);
105320           // const ang1 = Angle.angle(basePt, this._seg1.p1)
105321           var angDiff = Angle.angleBetweenOriented(this._seg0.p0, basePt, this._seg1.p1);
105322           var angDiffHalf = angDiff / 2;
105323           var midAng = Angle.normalize(ang0 + angDiffHalf);
105324           var mitreMidAng = Angle.normalize(midAng + Math.PI);
105325           var mitreDist = mitreLimit * distance;
105326           var bevelDelta = mitreDist * Math.abs(Math.sin(angDiffHalf));
105327           var bevelHalfLen = distance - bevelDelta;
105328           var bevelMidX = basePt.x + mitreDist * Math.cos(mitreMidAng);
105329           var bevelMidY = basePt.y + mitreDist * Math.sin(mitreMidAng);
105330           var bevelMidPt = new Coordinate(bevelMidX, bevelMidY);
105331           var mitreMidLine = new LineSegment(basePt, bevelMidPt);
105332           var bevelEndLeft = mitreMidLine.pointAlongOffset(1.0, bevelHalfLen);
105333           var bevelEndRight = mitreMidLine.pointAlongOffset(1.0, -bevelHalfLen);
105334           if (this._side === Position.LEFT) {
105335             this._segList.addPt(bevelEndLeft);
105336             this._segList.addPt(bevelEndRight);
105337           } else {
105338             this._segList.addPt(bevelEndRight);
105339             this._segList.addPt(bevelEndLeft);
105340           }
105341         };
105342         OffsetSegmentGenerator.prototype.computeOffsetSegment = function computeOffsetSegment (seg, side, distance, offset) {
105343           var sideSign = side === Position.LEFT ? 1 : -1;
105344           var dx = seg.p1.x - seg.p0.x;
105345           var dy = seg.p1.y - seg.p0.y;
105346           var len = Math.sqrt(dx * dx + dy * dy);
105347           var ux = sideSign * distance * dx / len;
105348           var uy = sideSign * distance * dy / len;
105349           offset.p0.x = seg.p0.x - uy;
105350           offset.p0.y = seg.p0.y + ux;
105351           offset.p1.x = seg.p1.x - uy;
105352           offset.p1.y = seg.p1.y + ux;
105353         };
105354         OffsetSegmentGenerator.prototype.addFilletArc = function addFilletArc (p, startAngle, endAngle, direction, radius) {
105355             var this$1 = this;
105356
105357           var directionFactor = direction === CGAlgorithms.CLOCKWISE ? -1 : 1;
105358           var totalAngle = Math.abs(startAngle - endAngle);
105359           var nSegs = Math.trunc(totalAngle / this._filletAngleQuantum + 0.5);
105360           if (nSegs < 1) { return null }
105361           var initAngle = 0.0;
105362           var currAngleInc = totalAngle / nSegs;
105363           var currAngle = initAngle;
105364           var pt = new Coordinate();
105365           while (currAngle < totalAngle) {
105366             var angle = startAngle + directionFactor * currAngle;
105367             pt.x = p.x + radius * Math.cos(angle);
105368             pt.y = p.y + radius * Math.sin(angle);
105369             this$1._segList.addPt(pt);
105370             currAngle += currAngleInc;
105371           }
105372         };
105373         OffsetSegmentGenerator.prototype.addInsideTurn = function addInsideTurn (orientation, addStartPoint) {
105374           this._li.computeIntersection(this._offset0.p0, this._offset0.p1, this._offset1.p0, this._offset1.p1);
105375           if (this._li.hasIntersection()) {
105376             this._segList.addPt(this._li.getIntersection(0));
105377           } else {
105378             this._hasNarrowConcaveAngle = true;
105379             if (this._offset0.p1.distance(this._offset1.p0) < this._distance * OffsetSegmentGenerator.INSIDE_TURN_VERTEX_SNAP_DISTANCE_FACTOR) {
105380               this._segList.addPt(this._offset0.p1);
105381             } else {
105382               this._segList.addPt(this._offset0.p1);
105383               if (this._closingSegLengthFactor > 0) {
105384                 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));
105385                 this._segList.addPt(mid0);
105386                 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));
105387                 this._segList.addPt(mid1);
105388               } else {
105389                 this._segList.addPt(this._s1);
105390               }
105391               this._segList.addPt(this._offset1.p0);
105392             }
105393           }
105394         };
105395         OffsetSegmentGenerator.prototype.createCircle = function createCircle (p) {
105396           var pt = new Coordinate(p.x + this._distance, p.y);
105397           this._segList.addPt(pt);
105398           this.addFilletArc(p, 0.0, 2.0 * Math.PI, -1, this._distance);
105399           this._segList.closeRing();
105400         };
105401         OffsetSegmentGenerator.prototype.addBevelJoin = function addBevelJoin (offset0, offset1) {
105402           this._segList.addPt(offset0.p1);
105403           this._segList.addPt(offset1.p0);
105404         };
105405         OffsetSegmentGenerator.prototype.init = function init (distance) {
105406           this._distance = distance;
105407           this._maxCurveSegmentError = distance * (1 - Math.cos(this._filletAngleQuantum / 2.0));
105408           this._segList = new OffsetSegmentString();
105409           this._segList.setPrecisionModel(this._precisionModel);
105410           this._segList.setMinimumVertexDistance(distance * OffsetSegmentGenerator.CURVE_VERTEX_SNAP_DISTANCE_FACTOR);
105411         };
105412         OffsetSegmentGenerator.prototype.addCollinear = function addCollinear (addStartPoint) {
105413           this._li.computeIntersection(this._s0, this._s1, this._s1, this._s2);
105414           var numInt = this._li.getIntersectionNum();
105415           if (numInt >= 2) {
105416             if (this._bufParams.getJoinStyle() === BufferParameters.JOIN_BEVEL || this._bufParams.getJoinStyle() === BufferParameters.JOIN_MITRE) {
105417               if (addStartPoint) { this._segList.addPt(this._offset0.p1); }
105418               this._segList.addPt(this._offset1.p0);
105419             } else {
105420               this.addFilletCorner(this._s1, this._offset0.p1, this._offset1.p0, CGAlgorithms.CLOCKWISE, this._distance);
105421             }
105422           }
105423         };
105424         OffsetSegmentGenerator.prototype.closeRing = function closeRing () {
105425           this._segList.closeRing();
105426         };
105427         OffsetSegmentGenerator.prototype.hasNarrowConcaveAngle = function hasNarrowConcaveAngle () {
105428           return this._hasNarrowConcaveAngle
105429         };
105430         OffsetSegmentGenerator.prototype.interfaces_ = function interfaces_ () {
105431           return []
105432         };
105433         OffsetSegmentGenerator.prototype.getClass = function getClass () {
105434           return OffsetSegmentGenerator
105435         };
105436         staticAccessors$27.OFFSET_SEGMENT_SEPARATION_FACTOR.get = function () { return 1.0E-3 };
105437         staticAccessors$27.INSIDE_TURN_VERTEX_SNAP_DISTANCE_FACTOR.get = function () { return 1.0E-3 };
105438         staticAccessors$27.CURVE_VERTEX_SNAP_DISTANCE_FACTOR.get = function () { return 1.0E-6 };
105439         staticAccessors$27.MAX_CLOSING_SEG_LEN_FACTOR.get = function () { return 80 };
105440
105441         Object.defineProperties( OffsetSegmentGenerator, staticAccessors$27 );
105442
105443         var OffsetCurveBuilder = function OffsetCurveBuilder () {
105444           this._distance = 0.0;
105445           this._precisionModel = null;
105446           this._bufParams = null;
105447           var precisionModel = arguments[0];
105448           var bufParams = arguments[1];
105449           this._precisionModel = precisionModel;
105450           this._bufParams = bufParams;
105451         };
105452         OffsetCurveBuilder.prototype.getOffsetCurve = function getOffsetCurve (inputPts, distance) {
105453           this._distance = distance;
105454           if (distance === 0.0) { return null }
105455           var isRightSide = distance < 0.0;
105456           var posDistance = Math.abs(distance);
105457           var segGen = this.getSegGen(posDistance);
105458           if (inputPts.length <= 1) {
105459             this.computePointCurve(inputPts[0], segGen);
105460           } else {
105461             this.computeOffsetCurve(inputPts, isRightSide, segGen);
105462           }
105463           var curvePts = segGen.getCoordinates();
105464           if (isRightSide) { CoordinateArrays.reverse(curvePts); }
105465           return curvePts
105466         };
105467         OffsetCurveBuilder.prototype.computeSingleSidedBufferCurve = function computeSingleSidedBufferCurve (inputPts, isRightSide, segGen) {
105468           var distTol = this.simplifyTolerance(this._distance);
105469           if (isRightSide) {
105470             segGen.addSegments(inputPts, true);
105471             var simp2 = BufferInputLineSimplifier.simplify(inputPts, -distTol);
105472             var n2 = simp2.length - 1;
105473             segGen.initSideSegments(simp2[n2], simp2[n2 - 1], Position.LEFT);
105474             segGen.addFirstSegment();
105475             for (var i = n2 - 2; i >= 0; i--) {
105476               segGen.addNextSegment(simp2[i], true);
105477             }
105478           } else {
105479             segGen.addSegments(inputPts, false);
105480             var simp1 = BufferInputLineSimplifier.simplify(inputPts, distTol);
105481             var n1 = simp1.length - 1;
105482             segGen.initSideSegments(simp1[0], simp1[1], Position.LEFT);
105483             segGen.addFirstSegment();
105484             for (var i$1 = 2; i$1 <= n1; i$1++) {
105485               segGen.addNextSegment(simp1[i$1], true);
105486             }
105487           }
105488           segGen.addLastSegment();
105489           segGen.closeRing();
105490         };
105491         OffsetCurveBuilder.prototype.computeRingBufferCurve = function computeRingBufferCurve (inputPts, side, segGen) {
105492           var distTol = this.simplifyTolerance(this._distance);
105493           if (side === Position.RIGHT) { distTol = -distTol; }
105494           var simp = BufferInputLineSimplifier.simplify(inputPts, distTol);
105495           var n = simp.length - 1;
105496           segGen.initSideSegments(simp[n - 1], simp[0], side);
105497           for (var i = 1; i <= n; i++) {
105498             var addStartPoint = i !== 1;
105499             segGen.addNextSegment(simp[i], addStartPoint);
105500           }
105501           segGen.closeRing();
105502         };
105503         OffsetCurveBuilder.prototype.computeLineBufferCurve = function computeLineBufferCurve (inputPts, segGen) {
105504           var distTol = this.simplifyTolerance(this._distance);
105505           var simp1 = BufferInputLineSimplifier.simplify(inputPts, distTol);
105506           var n1 = simp1.length - 1;
105507           segGen.initSideSegments(simp1[0], simp1[1], Position.LEFT);
105508           for (var i = 2; i <= n1; i++) {
105509             segGen.addNextSegment(simp1[i], true);
105510           }
105511           segGen.addLastSegment();
105512           segGen.addLineEndCap(simp1[n1 - 1], simp1[n1]);
105513           var simp2 = BufferInputLineSimplifier.simplify(inputPts, -distTol);
105514           var n2 = simp2.length - 1;
105515           segGen.initSideSegments(simp2[n2], simp2[n2 - 1], Position.LEFT);
105516           for (var i$1 = n2 - 2; i$1 >= 0; i$1--) {
105517             segGen.addNextSegment(simp2[i$1], true);
105518           }
105519           segGen.addLastSegment();
105520           segGen.addLineEndCap(simp2[1], simp2[0]);
105521           segGen.closeRing();
105522         };
105523         OffsetCurveBuilder.prototype.computePointCurve = function computePointCurve (pt, segGen) {
105524           switch (this._bufParams.getEndCapStyle()) {
105525             case BufferParameters.CAP_ROUND:
105526               segGen.createCircle(pt);
105527               break
105528             case BufferParameters.CAP_SQUARE:
105529               segGen.createSquare(pt);
105530               break
105531           }
105532         };
105533         OffsetCurveBuilder.prototype.getLineCurve = function getLineCurve (inputPts, distance) {
105534           this._distance = distance;
105535           if (distance < 0.0 && !this._bufParams.isSingleSided()) { return null }
105536           if (distance === 0.0) { return null }
105537           var posDistance = Math.abs(distance);
105538           var segGen = this.getSegGen(posDistance);
105539           if (inputPts.length <= 1) {
105540             this.computePointCurve(inputPts[0], segGen);
105541           } else {
105542             if (this._bufParams.isSingleSided()) {
105543               var isRightSide = distance < 0.0;
105544               this.computeSingleSidedBufferCurve(inputPts, isRightSide, segGen);
105545             } else { this.computeLineBufferCurve(inputPts, segGen); }
105546           }
105547           var lineCoord = segGen.getCoordinates();
105548           return lineCoord
105549         };
105550         OffsetCurveBuilder.prototype.getBufferParameters = function getBufferParameters () {
105551           return this._bufParams
105552         };
105553         OffsetCurveBuilder.prototype.simplifyTolerance = function simplifyTolerance (bufDistance) {
105554           return bufDistance * this._bufParams.getSimplifyFactor()
105555         };
105556         OffsetCurveBuilder.prototype.getRingCurve = function getRingCurve (inputPts, side, distance) {
105557           this._distance = distance;
105558           if (inputPts.length <= 2) { return this.getLineCurve(inputPts, distance) }
105559           if (distance === 0.0) {
105560             return OffsetCurveBuilder.copyCoordinates(inputPts)
105561           }
105562           var segGen = this.getSegGen(distance);
105563           this.computeRingBufferCurve(inputPts, side, segGen);
105564           return segGen.getCoordinates()
105565         };
105566         OffsetCurveBuilder.prototype.computeOffsetCurve = function computeOffsetCurve (inputPts, isRightSide, segGen) {
105567           var distTol = this.simplifyTolerance(this._distance);
105568           if (isRightSide) {
105569             var simp2 = BufferInputLineSimplifier.simplify(inputPts, -distTol);
105570             var n2 = simp2.length - 1;
105571             segGen.initSideSegments(simp2[n2], simp2[n2 - 1], Position.LEFT);
105572             segGen.addFirstSegment();
105573             for (var i = n2 - 2; i >= 0; i--) {
105574               segGen.addNextSegment(simp2[i], true);
105575             }
105576           } else {
105577             var simp1 = BufferInputLineSimplifier.simplify(inputPts, distTol);
105578             var n1 = simp1.length - 1;
105579             segGen.initSideSegments(simp1[0], simp1[1], Position.LEFT);
105580             segGen.addFirstSegment();
105581             for (var i$1 = 2; i$1 <= n1; i$1++) {
105582               segGen.addNextSegment(simp1[i$1], true);
105583             }
105584           }
105585           segGen.addLastSegment();
105586         };
105587         OffsetCurveBuilder.prototype.getSegGen = function getSegGen (distance) {
105588           return new OffsetSegmentGenerator(this._precisionModel, this._bufParams, distance)
105589         };
105590         OffsetCurveBuilder.prototype.interfaces_ = function interfaces_ () {
105591           return []
105592         };
105593         OffsetCurveBuilder.prototype.getClass = function getClass () {
105594           return OffsetCurveBuilder
105595         };
105596         OffsetCurveBuilder.copyCoordinates = function copyCoordinates (pts) {
105597           var copy = new Array(pts.length).fill(null);
105598           for (var i = 0; i < copy.length; i++) {
105599             copy[i] = new Coordinate(pts[i]);
105600           }
105601           return copy
105602         };
105603
105604         var SubgraphDepthLocater = function SubgraphDepthLocater () {
105605           this._subgraphs = null;
105606           this._seg = new LineSegment();
105607           this._cga = new CGAlgorithms();
105608           var subgraphs = arguments[0];
105609           this._subgraphs = subgraphs;
105610         };
105611
105612         var staticAccessors$30 = { DepthSegment: { configurable: true } };
105613         SubgraphDepthLocater.prototype.findStabbedSegments = function findStabbedSegments () {
105614             var this$1 = this;
105615
105616           if (arguments.length === 1) {
105617             var stabbingRayLeftPt = arguments[0];
105618             var stabbedSegments = new ArrayList();
105619             for (var i = this._subgraphs.iterator(); i.hasNext();) {
105620               var bsg = i.next();
105621               var env = bsg.getEnvelope();
105622               if (stabbingRayLeftPt.y < env.getMinY() || stabbingRayLeftPt.y > env.getMaxY()) { continue }
105623               this$1.findStabbedSegments(stabbingRayLeftPt, bsg.getDirectedEdges(), stabbedSegments);
105624             }
105625             return stabbedSegments
105626           } else if (arguments.length === 3) {
105627             if (hasInterface(arguments[2], List) && (arguments[0] instanceof Coordinate && arguments[1] instanceof DirectedEdge)) {
105628               var stabbingRayLeftPt$1 = arguments[0];
105629               var dirEdge = arguments[1];
105630               var stabbedSegments$1 = arguments[2];
105631               var pts = dirEdge.getEdge().getCoordinates();
105632               for (var i$1 = 0; i$1 < pts.length - 1; i$1++) {
105633                 this$1._seg.p0 = pts[i$1];
105634                 this$1._seg.p1 = pts[i$1 + 1];
105635                 if (this$1._seg.p0.y > this$1._seg.p1.y) { this$1._seg.reverse(); }
105636                 var maxx = Math.max(this$1._seg.p0.x, this$1._seg.p1.x);
105637                 if (maxx < stabbingRayLeftPt$1.x) { continue }
105638                 if (this$1._seg.isHorizontal()) { continue }
105639                 if (stabbingRayLeftPt$1.y < this$1._seg.p0.y || stabbingRayLeftPt$1.y > this$1._seg.p1.y) { continue }
105640                 if (CGAlgorithms.computeOrientation(this$1._seg.p0, this$1._seg.p1, stabbingRayLeftPt$1) === CGAlgorithms.RIGHT) { continue }
105641                 var depth = dirEdge.getDepth(Position.LEFT);
105642                 if (!this$1._seg.p0.equals(pts[i$1])) { depth = dirEdge.getDepth(Position.RIGHT); }
105643                 var ds = new DepthSegment(this$1._seg, depth);
105644                 stabbedSegments$1.add(ds);
105645               }
105646             } else if (hasInterface(arguments[2], List) && (arguments[0] instanceof Coordinate && hasInterface(arguments[1], List))) {
105647               var stabbingRayLeftPt$2 = arguments[0];
105648               var dirEdges = arguments[1];
105649               var stabbedSegments$2 = arguments[2];
105650               for (var i$2 = dirEdges.iterator(); i$2.hasNext();) {
105651                 var de = i$2.next();
105652                 if (!de.isForward()) { continue }
105653                 this$1.findStabbedSegments(stabbingRayLeftPt$2, de, stabbedSegments$2);
105654               }
105655             }
105656           }
105657         };
105658         SubgraphDepthLocater.prototype.getDepth = function getDepth (p) {
105659           var stabbedSegments = this.findStabbedSegments(p);
105660           if (stabbedSegments.size() === 0) { return 0 }
105661           var ds = Collections.min(stabbedSegments);
105662           return ds._leftDepth
105663         };
105664         SubgraphDepthLocater.prototype.interfaces_ = function interfaces_ () {
105665           return []
105666         };
105667         SubgraphDepthLocater.prototype.getClass = function getClass () {
105668           return SubgraphDepthLocater
105669         };
105670         staticAccessors$30.DepthSegment.get = function () { return DepthSegment };
105671
105672         Object.defineProperties( SubgraphDepthLocater, staticAccessors$30 );
105673
105674         var DepthSegment = function DepthSegment () {
105675           this._upwardSeg = null;
105676           this._leftDepth = null;
105677           var seg = arguments[0];
105678           var depth = arguments[1];
105679           this._upwardSeg = new LineSegment(seg);
105680           this._leftDepth = depth;
105681         };
105682         DepthSegment.prototype.compareTo = function compareTo (obj) {
105683           var other = obj;
105684           if (this._upwardSeg.minX() >= other._upwardSeg.maxX()) { return 1 }
105685           if (this._upwardSeg.maxX() <= other._upwardSeg.minX()) { return -1 }
105686           var orientIndex = this._upwardSeg.orientationIndex(other._upwardSeg);
105687           if (orientIndex !== 0) { return orientIndex }
105688           orientIndex = -1 * other._upwardSeg.orientationIndex(this._upwardSeg);
105689           if (orientIndex !== 0) { return orientIndex }
105690           return this._upwardSeg.compareTo(other._upwardSeg)
105691         };
105692         DepthSegment.prototype.compareX = function compareX (seg0, seg1) {
105693           var compare0 = seg0.p0.compareTo(seg1.p0);
105694           if (compare0 !== 0) { return compare0 }
105695           return seg0.p1.compareTo(seg1.p1)
105696         };
105697         DepthSegment.prototype.toString = function toString () {
105698           return this._upwardSeg.toString()
105699         };
105700         DepthSegment.prototype.interfaces_ = function interfaces_ () {
105701           return [Comparable]
105702         };
105703         DepthSegment.prototype.getClass = function getClass () {
105704           return DepthSegment
105705         };
105706
105707         var Triangle = function Triangle (p0, p1, p2) {
105708           this.p0 = p0 || null;
105709           this.p1 = p1 || null;
105710           this.p2 = p2 || null;
105711         };
105712         Triangle.prototype.area = function area () {
105713           return Triangle.area(this.p0, this.p1, this.p2)
105714         };
105715         Triangle.prototype.signedArea = function signedArea () {
105716           return Triangle.signedArea(this.p0, this.p1, this.p2)
105717         };
105718         Triangle.prototype.interpolateZ = function interpolateZ (p) {
105719           if (p === null) { throw new IllegalArgumentException('Supplied point is null.') }
105720           return Triangle.interpolateZ(p, this.p0, this.p1, this.p2)
105721         };
105722         Triangle.prototype.longestSideLength = function longestSideLength () {
105723           return Triangle.longestSideLength(this.p0, this.p1, this.p2)
105724         };
105725         Triangle.prototype.isAcute = function isAcute () {
105726           return Triangle.isAcute(this.p0, this.p1, this.p2)
105727         };
105728         Triangle.prototype.circumcentre = function circumcentre () {
105729           return Triangle.circumcentre(this.p0, this.p1, this.p2)
105730         };
105731         Triangle.prototype.area3D = function area3D () {
105732           return Triangle.area3D(this.p0, this.p1, this.p2)
105733         };
105734         Triangle.prototype.centroid = function centroid () {
105735           return Triangle.centroid(this.p0, this.p1, this.p2)
105736         };
105737         Triangle.prototype.inCentre = function inCentre () {
105738           return Triangle.inCentre(this.p0, this.p1, this.p2)
105739         };
105740         Triangle.prototype.interfaces_ = function interfaces_ () {
105741           return []
105742         };
105743         Triangle.prototype.getClass = function getClass () {
105744           return Triangle
105745         };
105746         Triangle.area = function area (a, b, c) {
105747           return Math.abs(((c.x - a.x) * (b.y - a.y) - (b.x - a.x) * (c.y - a.y)) / 2)
105748         };
105749         Triangle.signedArea = function signedArea (a, b, c) {
105750           return ((c.x - a.x) * (b.y - a.y) - (b.x - a.x) * (c.y - a.y)) / 2
105751         };
105752         Triangle.det = function det (m00, m01, m10, m11) {
105753           return m00 * m11 - m01 * m10
105754         };
105755         Triangle.interpolateZ = function interpolateZ (p, v0, v1, v2) {
105756           var x0 = v0.x;
105757           var y0 = v0.y;
105758           var a = v1.x - x0;
105759           var b = v2.x - x0;
105760           var c = v1.y - y0;
105761           var d = v2.y - y0;
105762           var det = a * d - b * c;
105763           var dx = p.x - x0;
105764           var dy = p.y - y0;
105765           var t = (d * dx - b * dy) / det;
105766           var u = (-c * dx + a * dy) / det;
105767           var z = v0.z + t * (v1.z - v0.z) + u * (v2.z - v0.z);
105768           return z
105769         };
105770         Triangle.longestSideLength = function longestSideLength (a, b, c) {
105771           var lenAB = a.distance(b);
105772           var lenBC = b.distance(c);
105773           var lenCA = c.distance(a);
105774           var maxLen = lenAB;
105775           if (lenBC > maxLen) { maxLen = lenBC; }
105776           if (lenCA > maxLen) { maxLen = lenCA; }
105777           return maxLen
105778         };
105779         Triangle.isAcute = function isAcute (a, b, c) {
105780           if (!Angle.isAcute(a, b, c)) { return false }
105781           if (!Angle.isAcute(b, c, a)) { return false }
105782           if (!Angle.isAcute(c, a, b)) { return false }
105783           return true
105784         };
105785         Triangle.circumcentre = function circumcentre (a, b, c) {
105786           var cx = c.x;
105787           var cy = c.y;
105788           var ax = a.x - cx;
105789           var ay = a.y - cy;
105790           var bx = b.x - cx;
105791           var by = b.y - cy;
105792           var denom = 2 * Triangle.det(ax, ay, bx, by);
105793           var numx = Triangle.det(ay, ax * ax + ay * ay, by, bx * bx + by * by);
105794           var numy = Triangle.det(ax, ax * ax + ay * ay, bx, bx * bx + by * by);
105795           var ccx = cx - numx / denom;
105796           var ccy = cy + numy / denom;
105797           return new Coordinate(ccx, ccy)
105798         };
105799         Triangle.perpendicularBisector = function perpendicularBisector (a, b) {
105800           var dx = b.x - a.x;
105801           var dy = b.y - a.y;
105802           var l1 = new HCoordinate(a.x + dx / 2.0, a.y + dy / 2.0, 1.0);
105803           var l2 = new HCoordinate(a.x - dy + dx / 2.0, a.y + dx + dy / 2.0, 1.0);
105804           return new HCoordinate(l1, l2)
105805         };
105806         Triangle.angleBisector = function angleBisector (a, b, c) {
105807           var len0 = b.distance(a);
105808           var len2 = b.distance(c);
105809           var frac = len0 / (len0 + len2);
105810           var dx = c.x - a.x;
105811           var dy = c.y - a.y;
105812           var splitPt = new Coordinate(a.x + frac * dx, a.y + frac * dy);
105813           return splitPt
105814         };
105815         Triangle.area3D = function area3D (a, b, c) {
105816           var ux = b.x - a.x;
105817           var uy = b.y - a.y;
105818           var uz = b.z - a.z;
105819           var vx = c.x - a.x;
105820           var vy = c.y - a.y;
105821           var vz = c.z - a.z;
105822           var crossx = uy * vz - uz * vy;
105823           var crossy = uz * vx - ux * vz;
105824           var crossz = ux * vy - uy * vx;
105825           var absSq = crossx * crossx + crossy * crossy + crossz * crossz;
105826           var area3D = Math.sqrt(absSq) / 2;
105827           return area3D
105828         };
105829         Triangle.centroid = function centroid (a, b, c) {
105830           var x = (a.x + b.x + c.x) / 3;
105831           var y = (a.y + b.y + c.y) / 3;
105832           return new Coordinate(x, y)
105833         };
105834         Triangle.inCentre = function inCentre (a, b, c) {
105835           var len0 = b.distance(c);
105836           var len1 = a.distance(c);
105837           var len2 = a.distance(b);
105838           var circum = len0 + len1 + len2;
105839           var inCentreX = (len0 * a.x + len1 * b.x + len2 * c.x) / circum;
105840           var inCentreY = (len0 * a.y + len1 * b.y + len2 * c.y) / circum;
105841           return new Coordinate(inCentreX, inCentreY)
105842         };
105843
105844         var OffsetCurveSetBuilder = function OffsetCurveSetBuilder () {
105845           this._inputGeom = null;
105846           this._distance = null;
105847           this._curveBuilder = null;
105848           this._curveList = new ArrayList();
105849           var inputGeom = arguments[0];
105850           var distance = arguments[1];
105851           var curveBuilder = arguments[2];
105852           this._inputGeom = inputGeom;
105853           this._distance = distance;
105854           this._curveBuilder = curveBuilder;
105855         };
105856         OffsetCurveSetBuilder.prototype.addPoint = function addPoint (p) {
105857           if (this._distance <= 0.0) { return null }
105858           var coord = p.getCoordinates();
105859           var curve = this._curveBuilder.getLineCurve(coord, this._distance);
105860           this.addCurve(curve, Location.EXTERIOR, Location.INTERIOR);
105861         };
105862         OffsetCurveSetBuilder.prototype.addPolygon = function addPolygon (p) {
105863             var this$1 = this;
105864
105865           var offsetDistance = this._distance;
105866           var offsetSide = Position.LEFT;
105867           if (this._distance < 0.0) {
105868             offsetDistance = -this._distance;
105869             offsetSide = Position.RIGHT;
105870           }
105871           var shell = p.getExteriorRing();
105872           var shellCoord = CoordinateArrays.removeRepeatedPoints(shell.getCoordinates());
105873           if (this._distance < 0.0 && this.isErodedCompletely(shell, this._distance)) { return null }
105874           if (this._distance <= 0.0 && shellCoord.length < 3) { return null }
105875           this.addPolygonRing(shellCoord, offsetDistance, offsetSide, Location.EXTERIOR, Location.INTERIOR);
105876           for (var i = 0; i < p.getNumInteriorRing(); i++) {
105877             var hole = p.getInteriorRingN(i);
105878             var holeCoord = CoordinateArrays.removeRepeatedPoints(hole.getCoordinates());
105879             if (this$1._distance > 0.0 && this$1.isErodedCompletely(hole, -this$1._distance)) { continue }
105880             this$1.addPolygonRing(holeCoord, offsetDistance, Position.opposite(offsetSide), Location.INTERIOR, Location.EXTERIOR);
105881           }
105882         };
105883         OffsetCurveSetBuilder.prototype.isTriangleErodedCompletely = function isTriangleErodedCompletely (triangleCoord, bufferDistance) {
105884           var tri = new Triangle(triangleCoord[0], triangleCoord[1], triangleCoord[2]);
105885           var inCentre = tri.inCentre();
105886           var distToCentre = CGAlgorithms.distancePointLine(inCentre, tri.p0, tri.p1);
105887           return distToCentre < Math.abs(bufferDistance)
105888         };
105889         OffsetCurveSetBuilder.prototype.addLineString = function addLineString (line) {
105890           if (this._distance <= 0.0 && !this._curveBuilder.getBufferParameters().isSingleSided()) { return null }
105891           var coord = CoordinateArrays.removeRepeatedPoints(line.getCoordinates());
105892           var curve = this._curveBuilder.getLineCurve(coord, this._distance);
105893           this.addCurve(curve, Location.EXTERIOR, Location.INTERIOR);
105894         };
105895         OffsetCurveSetBuilder.prototype.addCurve = function addCurve (coord, leftLoc, rightLoc) {
105896           if (coord === null || coord.length < 2) { return null }
105897           var e = new NodedSegmentString(coord, new Label(0, Location.BOUNDARY, leftLoc, rightLoc));
105898           this._curveList.add(e);
105899         };
105900         OffsetCurveSetBuilder.prototype.getCurves = function getCurves () {
105901           this.add(this._inputGeom);
105902           return this._curveList
105903         };
105904         OffsetCurveSetBuilder.prototype.addPolygonRing = function addPolygonRing (coord, offsetDistance, side, cwLeftLoc, cwRightLoc) {
105905           if (offsetDistance === 0.0 && coord.length < LinearRing.MINIMUM_VALID_SIZE) { return null }
105906           var leftLoc = cwLeftLoc;
105907           var rightLoc = cwRightLoc;
105908           if (coord.length >= LinearRing.MINIMUM_VALID_SIZE && CGAlgorithms.isCCW(coord)) {
105909             leftLoc = cwRightLoc;
105910             rightLoc = cwLeftLoc;
105911             side = Position.opposite(side);
105912           }
105913           var curve = this._curveBuilder.getRingCurve(coord, side, offsetDistance);
105914           this.addCurve(curve, leftLoc, rightLoc);
105915         };
105916         OffsetCurveSetBuilder.prototype.add = function add (g) {
105917           if (g.isEmpty()) { return null }
105918           if (g instanceof Polygon) { this.addPolygon(g); }
105919           else if (g instanceof LineString) { this.addLineString(g); }
105920           else if (g instanceof Point$1) { this.addPoint(g); }
105921           else if (g instanceof MultiPoint) { this.addCollection(g); }
105922           else if (g instanceof MultiLineString) { this.addCollection(g); }
105923           else if (g instanceof MultiPolygon) { this.addCollection(g); }
105924           else if (g instanceof GeometryCollection) { this.addCollection(g); }
105925           // else throw new UnsupportedOperationException(g.getClass().getName())
105926         };
105927         OffsetCurveSetBuilder.prototype.isErodedCompletely = function isErodedCompletely (ring, bufferDistance) {
105928           var ringCoord = ring.getCoordinates();
105929           // const minDiam = 0.0
105930           if (ringCoord.length < 4) { return bufferDistance < 0 }
105931           if (ringCoord.length === 4) { return this.isTriangleErodedCompletely(ringCoord, bufferDistance) }
105932           var env = ring.getEnvelopeInternal();
105933           var envMinDimension = Math.min(env.getHeight(), env.getWidth());
105934           if (bufferDistance < 0.0 && 2 * Math.abs(bufferDistance) > envMinDimension) { return true }
105935           return false
105936         };
105937         OffsetCurveSetBuilder.prototype.addCollection = function addCollection (gc) {
105938             var this$1 = this;
105939
105940           for (var i = 0; i < gc.getNumGeometries(); i++) {
105941             var g = gc.getGeometryN(i);
105942             this$1.add(g);
105943           }
105944         };
105945         OffsetCurveSetBuilder.prototype.interfaces_ = function interfaces_ () {
105946           return []
105947         };
105948         OffsetCurveSetBuilder.prototype.getClass = function getClass () {
105949           return OffsetCurveSetBuilder
105950         };
105951
105952         var PointOnGeometryLocator = function PointOnGeometryLocator () {};
105953
105954         PointOnGeometryLocator.prototype.locate = function locate (p) {};
105955         PointOnGeometryLocator.prototype.interfaces_ = function interfaces_ () {
105956           return []
105957         };
105958         PointOnGeometryLocator.prototype.getClass = function getClass () {
105959           return PointOnGeometryLocator
105960         };
105961
105962         var GeometryCollectionIterator = function GeometryCollectionIterator () {
105963           this._parent = null;
105964           this._atStart = null;
105965           this._max = null;
105966           this._index = null;
105967           this._subcollectionIterator = null;
105968           var parent = arguments[0];
105969           this._parent = parent;
105970           this._atStart = true;
105971           this._index = 0;
105972           this._max = parent.getNumGeometries();
105973         };
105974         GeometryCollectionIterator.prototype.next = function next () {
105975           if (this._atStart) {
105976             this._atStart = false;
105977             if (GeometryCollectionIterator.isAtomic(this._parent)) { this._index++; }
105978             return this._parent
105979           }
105980           if (this._subcollectionIterator !== null) {
105981             if (this._subcollectionIterator.hasNext()) {
105982               return this._subcollectionIterator.next()
105983             } else {
105984               this._subcollectionIterator = null;
105985             }
105986           }
105987           if (this._index >= this._max) {
105988             throw new NoSuchElementException()
105989           }
105990           var obj = this._parent.getGeometryN(this._index++);
105991           if (obj instanceof GeometryCollection) {
105992             this._subcollectionIterator = new GeometryCollectionIterator(obj);
105993             return this._subcollectionIterator.next()
105994           }
105995           return obj
105996         };
105997         GeometryCollectionIterator.prototype.remove = function remove () {
105998           throw new Error(this.getClass().getName())
105999         };
106000         GeometryCollectionIterator.prototype.hasNext = function hasNext () {
106001           if (this._atStart) {
106002             return true
106003           }
106004           if (this._subcollectionIterator !== null) {
106005             if (this._subcollectionIterator.hasNext()) {
106006               return true
106007             }
106008             this._subcollectionIterator = null;
106009           }
106010           if (this._index >= this._max) {
106011             return false
106012           }
106013           return true
106014         };
106015         GeometryCollectionIterator.prototype.interfaces_ = function interfaces_ () {
106016           return [Iterator$1]
106017         };
106018         GeometryCollectionIterator.prototype.getClass = function getClass () {
106019           return GeometryCollectionIterator
106020         };
106021         GeometryCollectionIterator.isAtomic = function isAtomic (geom) {
106022           return !(geom instanceof GeometryCollection)
106023         };
106024
106025         var SimplePointInAreaLocator = function SimplePointInAreaLocator () {
106026           this._geom = null;
106027           var geom = arguments[0];
106028           this._geom = geom;
106029         };
106030         SimplePointInAreaLocator.prototype.locate = function locate (p) {
106031           return SimplePointInAreaLocator.locate(p, this._geom)
106032         };
106033         SimplePointInAreaLocator.prototype.interfaces_ = function interfaces_ () {
106034           return [PointOnGeometryLocator]
106035         };
106036         SimplePointInAreaLocator.prototype.getClass = function getClass () {
106037           return SimplePointInAreaLocator
106038         };
106039         SimplePointInAreaLocator.isPointInRing = function isPointInRing (p, ring) {
106040           if (!ring.getEnvelopeInternal().intersects(p)) { return false }
106041           return CGAlgorithms.isPointInRing(p, ring.getCoordinates())
106042         };
106043         SimplePointInAreaLocator.containsPointInPolygon = function containsPointInPolygon (p, poly) {
106044           if (poly.isEmpty()) { return false }
106045           var shell = poly.getExteriorRing();
106046           if (!SimplePointInAreaLocator.isPointInRing(p, shell)) { return false }
106047           for (var i = 0; i < poly.getNumInteriorRing(); i++) {
106048             var hole = poly.getInteriorRingN(i);
106049             if (SimplePointInAreaLocator.isPointInRing(p, hole)) { return false }
106050           }
106051           return true
106052         };
106053         SimplePointInAreaLocator.containsPoint = function containsPoint (p, geom) {
106054           if (geom instanceof Polygon) {
106055             return SimplePointInAreaLocator.containsPointInPolygon(p, geom)
106056           } else if (geom instanceof GeometryCollection) {
106057             var geomi = new GeometryCollectionIterator(geom);
106058             while (geomi.hasNext()) {
106059               var g2 = geomi.next();
106060               if (g2 !== geom) { if (SimplePointInAreaLocator.containsPoint(p, g2)) { return true } }
106061             }
106062           }
106063           return false
106064         };
106065         SimplePointInAreaLocator.locate = function locate (p, geom) {
106066           if (geom.isEmpty()) { return Location.EXTERIOR }
106067           if (SimplePointInAreaLocator.containsPoint(p, geom)) { return Location.INTERIOR }
106068           return Location.EXTERIOR
106069         };
106070
106071         var EdgeEndStar = function EdgeEndStar () {
106072           this._edgeMap = new TreeMap();
106073           this._edgeList = null;
106074           this._ptInAreaLocation = [Location.NONE, Location.NONE];
106075         };
106076         EdgeEndStar.prototype.getNextCW = function getNextCW (ee) {
106077           this.getEdges();
106078           var i = this._edgeList.indexOf(ee);
106079           var iNextCW = i - 1;
106080           if (i === 0) { iNextCW = this._edgeList.size() - 1; }
106081           return this._edgeList.get(iNextCW)
106082         };
106083         EdgeEndStar.prototype.propagateSideLabels = function propagateSideLabels (geomIndex) {
106084           var startLoc = Location.NONE;
106085           for (var it = this.iterator(); it.hasNext();) {
106086             var e = it.next();
106087             var label = e.getLabel();
106088             if (label.isArea(geomIndex) && label.getLocation(geomIndex, Position.LEFT) !== Location.NONE) { startLoc = label.getLocation(geomIndex, Position.LEFT); }
106089           }
106090           if (startLoc === Location.NONE) { return null }
106091           var currLoc = startLoc;
106092           for (var it$1 = this.iterator(); it$1.hasNext();) {
106093             var e$1 = it$1.next();
106094             var label$1 = e$1.getLabel();
106095             if (label$1.getLocation(geomIndex, Position.ON) === Location.NONE) { label$1.setLocation(geomIndex, Position.ON, currLoc); }
106096             if (label$1.isArea(geomIndex)) {
106097               var leftLoc = label$1.getLocation(geomIndex, Position.LEFT);
106098               var rightLoc = label$1.getLocation(geomIndex, Position.RIGHT);
106099               if (rightLoc !== Location.NONE) {
106100                 if (rightLoc !== currLoc) { throw new TopologyException('side location conflict', e$1.getCoordinate()) }
106101                 if (leftLoc === Location.NONE) {
106102                   Assert.shouldNeverReachHere('found single null side (at ' + e$1.getCoordinate() + ')');
106103                 }
106104                 currLoc = leftLoc;
106105               } else {
106106                 Assert.isTrue(label$1.getLocation(geomIndex, Position.LEFT) === Location.NONE, 'found single null side');
106107                 label$1.setLocation(geomIndex, Position.RIGHT, currLoc);
106108                 label$1.setLocation(geomIndex, Position.LEFT, currLoc);
106109               }
106110             }
106111           }
106112         };
106113         EdgeEndStar.prototype.getCoordinate = function getCoordinate () {
106114           var it = this.iterator();
106115           if (!it.hasNext()) { return null }
106116           var e = it.next();
106117           return e.getCoordinate()
106118         };
106119         EdgeEndStar.prototype.print = function print (out) {
106120           System.out.println('EdgeEndStar:   ' + this.getCoordinate());
106121           for (var it = this.iterator(); it.hasNext();) {
106122             var e = it.next();
106123             e.print(out);
106124           }
106125         };
106126         EdgeEndStar.prototype.isAreaLabelsConsistent = function isAreaLabelsConsistent (geomGraph) {
106127           this.computeEdgeEndLabels(geomGraph.getBoundaryNodeRule());
106128           return this.checkAreaLabelsConsistent(0)
106129         };
106130         EdgeEndStar.prototype.checkAreaLabelsConsistent = function checkAreaLabelsConsistent (geomIndex) {
106131           var edges = this.getEdges();
106132           if (edges.size() <= 0) { return true }
106133           var lastEdgeIndex = edges.size() - 1;
106134           var startLabel = edges.get(lastEdgeIndex).getLabel();
106135           var startLoc = startLabel.getLocation(geomIndex, Position.LEFT);
106136           Assert.isTrue(startLoc !== Location.NONE, 'Found unlabelled area edge');
106137           var currLoc = startLoc;
106138           for (var it = this.iterator(); it.hasNext();) {
106139             var e = it.next();
106140             var label = e.getLabel();
106141             Assert.isTrue(label.isArea(geomIndex), 'Found non-area edge');
106142             var leftLoc = label.getLocation(geomIndex, Position.LEFT);
106143             var rightLoc = label.getLocation(geomIndex, Position.RIGHT);
106144             if (leftLoc === rightLoc) {
106145               return false
106146             }
106147             if (rightLoc !== currLoc) {
106148               return false
106149             }
106150             currLoc = leftLoc;
106151           }
106152           return true
106153         };
106154         EdgeEndStar.prototype.findIndex = function findIndex (eSearch) {
106155             var this$1 = this;
106156
106157           this.iterator();
106158           for (var i = 0; i < this._edgeList.size(); i++) {
106159             var e = this$1._edgeList.get(i);
106160             if (e === eSearch) { return i }
106161           }
106162           return -1
106163         };
106164         EdgeEndStar.prototype.iterator = function iterator () {
106165           return this.getEdges().iterator()
106166         };
106167         EdgeEndStar.prototype.getEdges = function getEdges () {
106168           if (this._edgeList === null) {
106169             this._edgeList = new ArrayList(this._edgeMap.values());
106170           }
106171           return this._edgeList
106172         };
106173         EdgeEndStar.prototype.getLocation = function getLocation (geomIndex, p, geom) {
106174           if (this._ptInAreaLocation[geomIndex] === Location.NONE) {
106175             this._ptInAreaLocation[geomIndex] = SimplePointInAreaLocator.locate(p, geom[geomIndex].getGeometry());
106176           }
106177           return this._ptInAreaLocation[geomIndex]
106178         };
106179         EdgeEndStar.prototype.toString = function toString () {
106180           var buf = new StringBuffer();
106181           buf.append('EdgeEndStar:   ' + this.getCoordinate());
106182           buf.append('\n');
106183           for (var it = this.iterator(); it.hasNext();) {
106184             var e = it.next();
106185             buf.append(e);
106186             buf.append('\n');
106187           }
106188           return buf.toString()
106189         };
106190         EdgeEndStar.prototype.computeEdgeEndLabels = function computeEdgeEndLabels (boundaryNodeRule) {
106191           for (var it = this.iterator(); it.hasNext();) {
106192             var ee = it.next();
106193             ee.computeLabel(boundaryNodeRule);
106194           }
106195         };
106196         EdgeEndStar.prototype.computeLabelling = function computeLabelling (geomGraph) {
106197             var this$1 = this;
106198
106199           this.computeEdgeEndLabels(geomGraph[0].getBoundaryNodeRule());
106200           this.propagateSideLabels(0);
106201           this.propagateSideLabels(1);
106202           var hasDimensionalCollapseEdge = [false, false];
106203           for (var it = this.iterator(); it.hasNext();) {
106204             var e = it.next();
106205             var label = e.getLabel();
106206             for (var geomi = 0; geomi < 2; geomi++) {
106207               if (label.isLine(geomi) && label.getLocation(geomi) === Location.BOUNDARY) { hasDimensionalCollapseEdge[geomi] = true; }
106208             }
106209           }
106210           for (var it$1 = this.iterator(); it$1.hasNext();) {
106211             var e$1 = it$1.next();
106212             var label$1 = e$1.getLabel();
106213             for (var geomi$1 = 0; geomi$1 < 2; geomi$1++) {
106214               if (label$1.isAnyNull(geomi$1)) {
106215                 var loc = Location.NONE;
106216                 if (hasDimensionalCollapseEdge[geomi$1]) {
106217                   loc = Location.EXTERIOR;
106218                 } else {
106219                   var p = e$1.getCoordinate();
106220                   loc = this$1.getLocation(geomi$1, p, geomGraph);
106221                 }
106222                 label$1.setAllLocationsIfNull(geomi$1, loc);
106223               }
106224             }
106225           }
106226         };
106227         EdgeEndStar.prototype.getDegree = function getDegree () {
106228           return this._edgeMap.size()
106229         };
106230         EdgeEndStar.prototype.insertEdgeEnd = function insertEdgeEnd (e, obj) {
106231           this._edgeMap.put(e, obj);
106232           this._edgeList = null;
106233         };
106234         EdgeEndStar.prototype.interfaces_ = function interfaces_ () {
106235           return []
106236         };
106237         EdgeEndStar.prototype.getClass = function getClass () {
106238           return EdgeEndStar
106239         };
106240
106241         var DirectedEdgeStar = (function (EdgeEndStar$$1) {
106242           function DirectedEdgeStar () {
106243             EdgeEndStar$$1.call(this);
106244             this._resultAreaEdgeList = null;
106245             this._label = null;
106246             this._SCANNING_FOR_INCOMING = 1;
106247             this._LINKING_TO_OUTGOING = 2;
106248           }
106249
106250           if ( EdgeEndStar$$1 ) { DirectedEdgeStar.__proto__ = EdgeEndStar$$1; }
106251           DirectedEdgeStar.prototype = Object.create( EdgeEndStar$$1 && EdgeEndStar$$1.prototype );
106252           DirectedEdgeStar.prototype.constructor = DirectedEdgeStar;
106253           DirectedEdgeStar.prototype.linkResultDirectedEdges = function linkResultDirectedEdges () {
106254             var this$1 = this;
106255
106256             this.getResultAreaEdges();
106257             var firstOut = null;
106258             var incoming = null;
106259             var state = this._SCANNING_FOR_INCOMING;
106260             for (var i = 0; i < this._resultAreaEdgeList.size(); i++) {
106261               var nextOut = this$1._resultAreaEdgeList.get(i);
106262               var nextIn = nextOut.getSym();
106263               if (!nextOut.getLabel().isArea()) { continue }
106264               if (firstOut === null && nextOut.isInResult()) { firstOut = nextOut; }
106265               switch (state) {
106266                 case this$1._SCANNING_FOR_INCOMING:
106267                   if (!nextIn.isInResult()) { continue }
106268                   incoming = nextIn;
106269                   state = this$1._LINKING_TO_OUTGOING;
106270                   break
106271                 case this$1._LINKING_TO_OUTGOING:
106272                   if (!nextOut.isInResult()) { continue }
106273                   incoming.setNext(nextOut);
106274                   state = this$1._SCANNING_FOR_INCOMING;
106275                   break
106276               }
106277             }
106278             if (state === this._LINKING_TO_OUTGOING) {
106279               if (firstOut === null) { throw new TopologyException('no outgoing dirEdge found', this.getCoordinate()) }
106280               Assert.isTrue(firstOut.isInResult(), 'unable to link last incoming dirEdge');
106281               incoming.setNext(firstOut);
106282             }
106283           };
106284           DirectedEdgeStar.prototype.insert = function insert (ee) {
106285             var de = ee;
106286             this.insertEdgeEnd(de, de);
106287           };
106288           DirectedEdgeStar.prototype.getRightmostEdge = function getRightmostEdge () {
106289             var edges = this.getEdges();
106290             var size = edges.size();
106291             if (size < 1) { return null }
106292             var de0 = edges.get(0);
106293             if (size === 1) { return de0 }
106294             var deLast = edges.get(size - 1);
106295             var quad0 = de0.getQuadrant();
106296             var quad1 = deLast.getQuadrant();
106297             if (Quadrant.isNorthern(quad0) && Quadrant.isNorthern(quad1)) { return de0; } else if (!Quadrant.isNorthern(quad0) && !Quadrant.isNorthern(quad1)) { return deLast; } else {
106298               // const nonHorizontalEdge = null
106299               if (de0.getDy() !== 0) { return de0; } else if (deLast.getDy() !== 0) { return deLast }
106300             }
106301             Assert.shouldNeverReachHere('found two horizontal edges incident on node');
106302             return null
106303           };
106304           DirectedEdgeStar.prototype.print = function print (out) {
106305             System.out.println('DirectedEdgeStar: ' + this.getCoordinate());
106306             for (var it = this.iterator(); it.hasNext();) {
106307               var de = it.next();
106308               out.print('out ');
106309               de.print(out);
106310               out.println();
106311               out.print('in ');
106312               de.getSym().print(out);
106313               out.println();
106314             }
106315           };
106316           DirectedEdgeStar.prototype.getResultAreaEdges = function getResultAreaEdges () {
106317             var this$1 = this;
106318
106319             if (this._resultAreaEdgeList !== null) { return this._resultAreaEdgeList }
106320             this._resultAreaEdgeList = new ArrayList();
106321             for (var it = this.iterator(); it.hasNext();) {
106322               var de = it.next();
106323               if (de.isInResult() || de.getSym().isInResult()) { this$1._resultAreaEdgeList.add(de); }
106324             }
106325             return this._resultAreaEdgeList
106326           };
106327           DirectedEdgeStar.prototype.updateLabelling = function updateLabelling (nodeLabel) {
106328             for (var it = this.iterator(); it.hasNext();) {
106329               var de = it.next();
106330               var label = de.getLabel();
106331               label.setAllLocationsIfNull(0, nodeLabel.getLocation(0));
106332               label.setAllLocationsIfNull(1, nodeLabel.getLocation(1));
106333             }
106334           };
106335           DirectedEdgeStar.prototype.linkAllDirectedEdges = function linkAllDirectedEdges () {
106336             var this$1 = this;
106337
106338             this.getEdges();
106339             var prevOut = null;
106340             var firstIn = null;
106341             for (var i = this._edgeList.size() - 1; i >= 0; i--) {
106342               var nextOut = this$1._edgeList.get(i);
106343               var nextIn = nextOut.getSym();
106344               if (firstIn === null) { firstIn = nextIn; }
106345               if (prevOut !== null) { nextIn.setNext(prevOut); }
106346               prevOut = nextOut;
106347             }
106348             firstIn.setNext(prevOut);
106349           };
106350           DirectedEdgeStar.prototype.computeDepths = function computeDepths () {
106351             var this$1 = this;
106352
106353             if (arguments.length === 1) {
106354               var de = arguments[0];
106355               var edgeIndex = this.findIndex(de);
106356               // const label = de.getLabel()
106357               var startDepth = de.getDepth(Position.LEFT);
106358               var targetLastDepth = de.getDepth(Position.RIGHT);
106359               var nextDepth = this.computeDepths(edgeIndex + 1, this._edgeList.size(), startDepth);
106360               var lastDepth = this.computeDepths(0, edgeIndex, nextDepth);
106361               if (lastDepth !== targetLastDepth) { throw new TopologyException('depth mismatch at ' + de.getCoordinate()) }
106362             } else if (arguments.length === 3) {
106363               var startIndex = arguments[0];
106364               var endIndex = arguments[1];
106365               var startDepth$1 = arguments[2];
106366               var currDepth = startDepth$1;
106367               for (var i = startIndex; i < endIndex; i++) {
106368                 var nextDe = this$1._edgeList.get(i);
106369                 // const label = nextDe.getLabel()
106370                 nextDe.setEdgeDepths(Position.RIGHT, currDepth);
106371                 currDepth = nextDe.getDepth(Position.LEFT);
106372               }
106373               return currDepth
106374             }
106375           };
106376           DirectedEdgeStar.prototype.mergeSymLabels = function mergeSymLabels () {
106377             for (var it = this.iterator(); it.hasNext();) {
106378               var de = it.next();
106379               var label = de.getLabel();
106380               label.merge(de.getSym().getLabel());
106381             }
106382           };
106383           DirectedEdgeStar.prototype.linkMinimalDirectedEdges = function linkMinimalDirectedEdges (er) {
106384             var this$1 = this;
106385
106386             var firstOut = null;
106387             var incoming = null;
106388             var state = this._SCANNING_FOR_INCOMING;
106389             for (var i = this._resultAreaEdgeList.size() - 1; i >= 0; i--) {
106390               var nextOut = this$1._resultAreaEdgeList.get(i);
106391               var nextIn = nextOut.getSym();
106392               if (firstOut === null && nextOut.getEdgeRing() === er) { firstOut = nextOut; }
106393               switch (state) {
106394                 case this$1._SCANNING_FOR_INCOMING:
106395                   if (nextIn.getEdgeRing() !== er) { continue }
106396                   incoming = nextIn;
106397                   state = this$1._LINKING_TO_OUTGOING;
106398                   break
106399                 case this$1._LINKING_TO_OUTGOING:
106400                   if (nextOut.getEdgeRing() !== er) { continue }
106401                   incoming.setNextMin(nextOut);
106402                   state = this$1._SCANNING_FOR_INCOMING;
106403                   break
106404               }
106405             }
106406             if (state === this._LINKING_TO_OUTGOING) {
106407               Assert.isTrue(firstOut !== null, 'found null for first outgoing dirEdge');
106408               Assert.isTrue(firstOut.getEdgeRing() === er, 'unable to link last incoming dirEdge');
106409               incoming.setNextMin(firstOut);
106410             }
106411           };
106412           DirectedEdgeStar.prototype.getOutgoingDegree = function getOutgoingDegree () {
106413             if (arguments.length === 0) {
106414               var degree = 0;
106415               for (var it = this.iterator(); it.hasNext();) {
106416                 var de = it.next();
106417                 if (de.isInResult()) { degree++; }
106418               }
106419               return degree
106420             } else if (arguments.length === 1) {
106421               var er = arguments[0];
106422               var degree$1 = 0;
106423               for (var it$1 = this.iterator(); it$1.hasNext();) {
106424                 var de$1 = it$1.next();
106425                 if (de$1.getEdgeRing() === er) { degree$1++; }
106426               }
106427               return degree$1
106428             }
106429           };
106430           DirectedEdgeStar.prototype.getLabel = function getLabel () {
106431             return this._label
106432           };
106433           DirectedEdgeStar.prototype.findCoveredLineEdges = function findCoveredLineEdges () {
106434             var startLoc = Location.NONE;
106435             for (var it = this.iterator(); it.hasNext();) {
106436               var nextOut = it.next();
106437               var nextIn = nextOut.getSym();
106438               if (!nextOut.isLineEdge()) {
106439                 if (nextOut.isInResult()) {
106440                   startLoc = Location.INTERIOR;
106441                   break
106442                 }
106443                 if (nextIn.isInResult()) {
106444                   startLoc = Location.EXTERIOR;
106445                   break
106446                 }
106447               }
106448             }
106449             if (startLoc === Location.NONE) { return null }
106450             var currLoc = startLoc;
106451             for (var it$1 = this.iterator(); it$1.hasNext();) {
106452               var nextOut$1 = it$1.next();
106453               var nextIn$1 = nextOut$1.getSym();
106454               if (nextOut$1.isLineEdge()) {
106455                 nextOut$1.getEdge().setCovered(currLoc === Location.INTERIOR);
106456               } else {
106457                 if (nextOut$1.isInResult()) { currLoc = Location.EXTERIOR; }
106458                 if (nextIn$1.isInResult()) { currLoc = Location.INTERIOR; }
106459               }
106460             }
106461           };
106462           DirectedEdgeStar.prototype.computeLabelling = function computeLabelling (geom) {
106463             var this$1 = this;
106464
106465             EdgeEndStar$$1.prototype.computeLabelling.call(this, geom);
106466             this._label = new Label(Location.NONE);
106467             for (var it = this.iterator(); it.hasNext();) {
106468               var ee = it.next();
106469               var e = ee.getEdge();
106470               var eLabel = e.getLabel();
106471               for (var i = 0; i < 2; i++) {
106472                 var eLoc = eLabel.getLocation(i);
106473                 if (eLoc === Location.INTERIOR || eLoc === Location.BOUNDARY) { this$1._label.setLocation(i, Location.INTERIOR); }
106474               }
106475             }
106476           };
106477           DirectedEdgeStar.prototype.interfaces_ = function interfaces_ () {
106478             return []
106479           };
106480           DirectedEdgeStar.prototype.getClass = function getClass () {
106481             return DirectedEdgeStar
106482           };
106483
106484           return DirectedEdgeStar;
106485         }(EdgeEndStar));
106486
106487         var OverlayNodeFactory = (function (NodeFactory$$1) {
106488           function OverlayNodeFactory () {
106489             NodeFactory$$1.apply(this, arguments);
106490           }
106491
106492           if ( NodeFactory$$1 ) { OverlayNodeFactory.__proto__ = NodeFactory$$1; }
106493           OverlayNodeFactory.prototype = Object.create( NodeFactory$$1 && NodeFactory$$1.prototype );
106494           OverlayNodeFactory.prototype.constructor = OverlayNodeFactory;
106495
106496           OverlayNodeFactory.prototype.createNode = function createNode (coord) {
106497             return new Node$1(coord, new DirectedEdgeStar())
106498           };
106499           OverlayNodeFactory.prototype.interfaces_ = function interfaces_ () {
106500             return []
106501           };
106502           OverlayNodeFactory.prototype.getClass = function getClass () {
106503             return OverlayNodeFactory
106504           };
106505
106506           return OverlayNodeFactory;
106507         }(NodeFactory));
106508
106509         var OrientedCoordinateArray = function OrientedCoordinateArray () {
106510           this._pts = null;
106511           this._orientation = null;
106512           var pts = arguments[0];
106513           this._pts = pts;
106514           this._orientation = OrientedCoordinateArray.orientation(pts);
106515         };
106516         OrientedCoordinateArray.prototype.compareTo = function compareTo (o1) {
106517           var oca = o1;
106518           var comp = OrientedCoordinateArray.compareOriented(this._pts, this._orientation, oca._pts, oca._orientation);
106519           return comp
106520         };
106521         OrientedCoordinateArray.prototype.interfaces_ = function interfaces_ () {
106522           return [Comparable]
106523         };
106524         OrientedCoordinateArray.prototype.getClass = function getClass () {
106525           return OrientedCoordinateArray
106526         };
106527         OrientedCoordinateArray.orientation = function orientation (pts) {
106528           return CoordinateArrays.increasingDirection(pts) === 1
106529         };
106530         OrientedCoordinateArray.compareOriented = function compareOriented (pts1, orientation1, pts2, orientation2) {
106531           var dir1 = orientation1 ? 1 : -1;
106532           var dir2 = orientation2 ? 1 : -1;
106533           var limit1 = orientation1 ? pts1.length : -1;
106534           var limit2 = orientation2 ? pts2.length : -1;
106535           var i1 = orientation1 ? 0 : pts1.length - 1;
106536           var i2 = orientation2 ? 0 : pts2.length - 1;
106537           // const comp = 0
106538           while (true) {
106539             var compPt = pts1[i1].compareTo(pts2[i2]);
106540             if (compPt !== 0) { return compPt }
106541             i1 += dir1;
106542             i2 += dir2;
106543             var done1 = i1 === limit1;
106544             var done2 = i2 === limit2;
106545             if (done1 && !done2) { return -1 }
106546             if (!done1 && done2) { return 1 }
106547             if (done1 && done2) { return 0 }
106548           }
106549         };
106550
106551         var EdgeList = function EdgeList () {
106552           this._edges = new ArrayList();
106553           this._ocaMap = new TreeMap();
106554         };
106555         EdgeList.prototype.print = function print (out) {
106556             var this$1 = this;
106557
106558           out.print('MULTILINESTRING ( ');
106559           for (var j = 0; j < this._edges.size(); j++) {
106560             var e = this$1._edges.get(j);
106561             if (j > 0) { out.print(','); }
106562             out.print('(');
106563             var pts = e.getCoordinates();
106564             for (var i = 0; i < pts.length; i++) {
106565               if (i > 0) { out.print(','); }
106566               out.print(pts[i].x + ' ' + pts[i].y);
106567             }
106568             out.println(')');
106569           }
106570           out.print(')  ');
106571         };
106572         EdgeList.prototype.addAll = function addAll (edgeColl) {
106573             var this$1 = this;
106574
106575           for (var i = edgeColl.iterator(); i.hasNext();) {
106576             this$1.add(i.next());
106577           }
106578         };
106579         EdgeList.prototype.findEdgeIndex = function findEdgeIndex (e) {
106580             var this$1 = this;
106581
106582           for (var i = 0; i < this._edges.size(); i++) {
106583             if (this$1._edges.get(i).equals(e)) { return i }
106584           }
106585           return -1
106586         };
106587         EdgeList.prototype.iterator = function iterator () {
106588           return this._edges.iterator()
106589         };
106590         EdgeList.prototype.getEdges = function getEdges () {
106591           return this._edges
106592         };
106593         EdgeList.prototype.get = function get (i) {
106594           return this._edges.get(i)
106595         };
106596         EdgeList.prototype.findEqualEdge = function findEqualEdge (e) {
106597           var oca = new OrientedCoordinateArray(e.getCoordinates());
106598           var matchEdge = this._ocaMap.get(oca);
106599           return matchEdge
106600         };
106601         EdgeList.prototype.add = function add (e) {
106602           this._edges.add(e);
106603           var oca = new OrientedCoordinateArray(e.getCoordinates());
106604           this._ocaMap.put(oca, e);
106605         };
106606         EdgeList.prototype.interfaces_ = function interfaces_ () {
106607           return []
106608         };
106609         EdgeList.prototype.getClass = function getClass () {
106610           return EdgeList
106611         };
106612
106613         var SegmentIntersector = function SegmentIntersector () {};
106614
106615         SegmentIntersector.prototype.processIntersections = function processIntersections (e0, segIndex0, e1, segIndex1) {};
106616         SegmentIntersector.prototype.isDone = function isDone () {};
106617         SegmentIntersector.prototype.interfaces_ = function interfaces_ () {
106618           return []
106619         };
106620         SegmentIntersector.prototype.getClass = function getClass () {
106621           return SegmentIntersector
106622         };
106623
106624         var IntersectionAdder = function IntersectionAdder () {
106625           this._hasIntersection = false;
106626           this._hasProper = false;
106627           this._hasProperInterior = false;
106628           this._hasInterior = false;
106629           this._properIntersectionPoint = null;
106630           this._li = null;
106631           this._isSelfIntersection = null;
106632           this.numIntersections = 0;
106633           this.numInteriorIntersections = 0;
106634           this.numProperIntersections = 0;
106635           this.numTests = 0;
106636           var li = arguments[0];
106637           this._li = li;
106638         };
106639         IntersectionAdder.prototype.isTrivialIntersection = function isTrivialIntersection (e0, segIndex0, e1, segIndex1) {
106640           if (e0 === e1) {
106641             if (this._li.getIntersectionNum() === 1) {
106642               if (IntersectionAdder.isAdjacentSegments(segIndex0, segIndex1)) { return true }
106643               if (e0.isClosed()) {
106644                 var maxSegIndex = e0.size() - 1;
106645                 if ((segIndex0 === 0 && segIndex1 === maxSegIndex) ||
106646                     (segIndex1 === 0 && segIndex0 === maxSegIndex)) {
106647                   return true
106648                 }
106649               }
106650             }
106651           }
106652           return false
106653         };
106654         IntersectionAdder.prototype.getProperIntersectionPoint = function getProperIntersectionPoint () {
106655           return this._properIntersectionPoint
106656         };
106657         IntersectionAdder.prototype.hasProperInteriorIntersection = function hasProperInteriorIntersection () {
106658           return this._hasProperInterior
106659         };
106660         IntersectionAdder.prototype.getLineIntersector = function getLineIntersector () {
106661           return this._li
106662         };
106663         IntersectionAdder.prototype.hasProperIntersection = function hasProperIntersection () {
106664           return this._hasProper
106665         };
106666         IntersectionAdder.prototype.processIntersections = function processIntersections (e0, segIndex0, e1, segIndex1) {
106667           if (e0 === e1 && segIndex0 === segIndex1) { return null }
106668           this.numTests++;
106669           var p00 = e0.getCoordinates()[segIndex0];
106670           var p01 = e0.getCoordinates()[segIndex0 + 1];
106671           var p10 = e1.getCoordinates()[segIndex1];
106672           var p11 = e1.getCoordinates()[segIndex1 + 1];
106673           this._li.computeIntersection(p00, p01, p10, p11);
106674           if (this._li.hasIntersection()) {
106675             this.numIntersections++;
106676             if (this._li.isInteriorIntersection()) {
106677               this.numInteriorIntersections++;
106678               this._hasInterior = true;
106679             }
106680             if (!this.isTrivialIntersection(e0, segIndex0, e1, segIndex1)) {
106681               this._hasIntersection = true;
106682               e0.addIntersections(this._li, segIndex0, 0);
106683               e1.addIntersections(this._li, segIndex1, 1);
106684               if (this._li.isProper()) {
106685                 this.numProperIntersections++;
106686                 this._hasProper = true;
106687                 this._hasProperInterior = true;
106688               }
106689             }
106690           }
106691         };
106692         IntersectionAdder.prototype.hasIntersection = function hasIntersection () {
106693           return this._hasIntersection
106694         };
106695         IntersectionAdder.prototype.isDone = function isDone () {
106696           return false
106697         };
106698         IntersectionAdder.prototype.hasInteriorIntersection = function hasInteriorIntersection () {
106699           return this._hasInterior
106700         };
106701         IntersectionAdder.prototype.interfaces_ = function interfaces_ () {
106702           return [SegmentIntersector]
106703         };
106704         IntersectionAdder.prototype.getClass = function getClass () {
106705           return IntersectionAdder
106706         };
106707         IntersectionAdder.isAdjacentSegments = function isAdjacentSegments (i1, i2) {
106708           return Math.abs(i1 - i2) === 1
106709         };
106710
106711         var EdgeIntersection = function EdgeIntersection () {
106712           this.coord = null;
106713           this.segmentIndex = null;
106714           this.dist = null;
106715           var coord = arguments[0];
106716           var segmentIndex = arguments[1];
106717           var dist = arguments[2];
106718           this.coord = new Coordinate(coord);
106719           this.segmentIndex = segmentIndex;
106720           this.dist = dist;
106721         };
106722         EdgeIntersection.prototype.getSegmentIndex = function getSegmentIndex () {
106723           return this.segmentIndex
106724         };
106725         EdgeIntersection.prototype.getCoordinate = function getCoordinate () {
106726           return this.coord
106727         };
106728         EdgeIntersection.prototype.print = function print (out) {
106729           out.print(this.coord);
106730           out.print(' seg # = ' + this.segmentIndex);
106731           out.println(' dist = ' + this.dist);
106732         };
106733         EdgeIntersection.prototype.compareTo = function compareTo (obj) {
106734           var other = obj;
106735           return this.compare(other.segmentIndex, other.dist)
106736         };
106737         EdgeIntersection.prototype.isEndPoint = function isEndPoint (maxSegmentIndex) {
106738           if (this.segmentIndex === 0 && this.dist === 0.0) { return true }
106739           if (this.segmentIndex === maxSegmentIndex) { return true }
106740           return false
106741         };
106742         EdgeIntersection.prototype.toString = function toString () {
106743           return this.coord + ' seg # = ' + this.segmentIndex + ' dist = ' + this.dist
106744         };
106745         EdgeIntersection.prototype.getDistance = function getDistance () {
106746           return this.dist
106747         };
106748         EdgeIntersection.prototype.compare = function compare (segmentIndex, dist) {
106749           if (this.segmentIndex < segmentIndex) { return -1 }
106750           if (this.segmentIndex > segmentIndex) { return 1 }
106751           if (this.dist < dist) { return -1 }
106752           if (this.dist > dist) { return 1 }
106753           return 0
106754         };
106755         EdgeIntersection.prototype.interfaces_ = function interfaces_ () {
106756           return [Comparable]
106757         };
106758         EdgeIntersection.prototype.getClass = function getClass () {
106759           return EdgeIntersection
106760         };
106761
106762         var EdgeIntersectionList = function EdgeIntersectionList () {
106763           this._nodeMap = new TreeMap();
106764           this.edge = null;
106765           var edge = arguments[0];
106766           this.edge = edge;
106767         };
106768         EdgeIntersectionList.prototype.print = function print (out) {
106769           out.println('Intersections:');
106770           for (var it = this.iterator(); it.hasNext();) {
106771             var ei = it.next();
106772             ei.print(out);
106773           }
106774         };
106775         EdgeIntersectionList.prototype.iterator = function iterator () {
106776           return this._nodeMap.values().iterator()
106777         };
106778         EdgeIntersectionList.prototype.addSplitEdges = function addSplitEdges (edgeList) {
106779             var this$1 = this;
106780
106781           this.addEndpoints();
106782           var it = this.iterator();
106783           var eiPrev = it.next();
106784           while (it.hasNext()) {
106785             var ei = it.next();
106786             var newEdge = this$1.createSplitEdge(eiPrev, ei);
106787             edgeList.add(newEdge);
106788             eiPrev = ei;
106789           }
106790         };
106791         EdgeIntersectionList.prototype.addEndpoints = function addEndpoints () {
106792           var maxSegIndex = this.edge.pts.length - 1;
106793           this.add(this.edge.pts[0], 0, 0.0);
106794           this.add(this.edge.pts[maxSegIndex], maxSegIndex, 0.0);
106795         };
106796         EdgeIntersectionList.prototype.createSplitEdge = function createSplitEdge (ei0, ei1) {
106797             var this$1 = this;
106798
106799           var npts = ei1.segmentIndex - ei0.segmentIndex + 2;
106800           var lastSegStartPt = this.edge.pts[ei1.segmentIndex];
106801           var useIntPt1 = ei1.dist > 0.0 || !ei1.coord.equals2D(lastSegStartPt);
106802           if (!useIntPt1) {
106803             npts--;
106804           }
106805           var pts = new Array(npts).fill(null);
106806           var ipt = 0;
106807           pts[ipt++] = new Coordinate(ei0.coord);
106808           for (var i = ei0.segmentIndex + 1; i <= ei1.segmentIndex; i++) {
106809             pts[ipt++] = this$1.edge.pts[i];
106810           }
106811           if (useIntPt1) { pts[ipt] = ei1.coord; }
106812           return new Edge(pts, new Label(this.edge._label))
106813         };
106814         EdgeIntersectionList.prototype.add = function add (intPt, segmentIndex, dist) {
106815           var eiNew = new EdgeIntersection(intPt, segmentIndex, dist);
106816           var ei = this._nodeMap.get(eiNew);
106817           if (ei !== null) {
106818             return ei
106819           }
106820           this._nodeMap.put(eiNew, eiNew);
106821           return eiNew
106822         };
106823         EdgeIntersectionList.prototype.isIntersection = function isIntersection (pt) {
106824           for (var it = this.iterator(); it.hasNext();) {
106825             var ei = it.next();
106826             if (ei.coord.equals(pt)) { return true }
106827           }
106828           return false
106829         };
106830         EdgeIntersectionList.prototype.interfaces_ = function interfaces_ () {
106831           return []
106832         };
106833         EdgeIntersectionList.prototype.getClass = function getClass () {
106834           return EdgeIntersectionList
106835         };
106836
106837         var MonotoneChainIndexer = function MonotoneChainIndexer () {};
106838
106839         MonotoneChainIndexer.prototype.getChainStartIndices = function getChainStartIndices (pts) {
106840             var this$1 = this;
106841
106842           var start = 0;
106843           var startIndexList = new ArrayList();
106844           startIndexList.add(new Integer(start));
106845           do {
106846             var last = this$1.findChainEnd(pts, start);
106847             startIndexList.add(new Integer(last));
106848             start = last;
106849           } while (start < pts.length - 1)
106850           var startIndex = MonotoneChainIndexer.toIntArray(startIndexList);
106851           return startIndex
106852         };
106853         MonotoneChainIndexer.prototype.findChainEnd = function findChainEnd (pts, start) {
106854           var chainQuad = Quadrant.quadrant(pts[start], pts[start + 1]);
106855           var last = start + 1;
106856           while (last < pts.length) {
106857             var quad = Quadrant.quadrant(pts[last - 1], pts[last]);
106858             if (quad !== chainQuad) { break }
106859             last++;
106860           }
106861           return last - 1
106862         };
106863         MonotoneChainIndexer.prototype.interfaces_ = function interfaces_ () {
106864           return []
106865         };
106866         MonotoneChainIndexer.prototype.getClass = function getClass () {
106867           return MonotoneChainIndexer
106868         };
106869         MonotoneChainIndexer.toIntArray = function toIntArray (list) {
106870           var array = new Array(list.size()).fill(null);
106871           for (var i = 0; i < array.length; i++) {
106872             array[i] = list.get(i).intValue();
106873           }
106874           return array
106875         };
106876
106877         var MonotoneChainEdge = function MonotoneChainEdge () {
106878           this.e = null;
106879           this.pts = null;
106880           this.startIndex = null;
106881           this.env1 = new Envelope();
106882           this.env2 = new Envelope();
106883           var e = arguments[0];
106884           this.e = e;
106885           this.pts = e.getCoordinates();
106886           var mcb = new MonotoneChainIndexer();
106887           this.startIndex = mcb.getChainStartIndices(this.pts);
106888         };
106889         MonotoneChainEdge.prototype.getCoordinates = function getCoordinates () {
106890           return this.pts
106891         };
106892         MonotoneChainEdge.prototype.getMaxX = function getMaxX (chainIndex) {
106893           var x1 = this.pts[this.startIndex[chainIndex]].x;
106894           var x2 = this.pts[this.startIndex[chainIndex + 1]].x;
106895           return x1 > x2 ? x1 : x2
106896         };
106897         MonotoneChainEdge.prototype.getMinX = function getMinX (chainIndex) {
106898           var x1 = this.pts[this.startIndex[chainIndex]].x;
106899           var x2 = this.pts[this.startIndex[chainIndex + 1]].x;
106900           return x1 < x2 ? x1 : x2
106901         };
106902         MonotoneChainEdge.prototype.computeIntersectsForChain = function computeIntersectsForChain () {
106903           if (arguments.length === 4) {
106904             var chainIndex0 = arguments[0];
106905             var mce = arguments[1];
106906             var chainIndex1 = arguments[2];
106907             var si = arguments[3];
106908             this.computeIntersectsForChain(this.startIndex[chainIndex0], this.startIndex[chainIndex0 + 1], mce, mce.startIndex[chainIndex1], mce.startIndex[chainIndex1 + 1], si);
106909           } else if (arguments.length === 6) {
106910             var start0 = arguments[0];
106911             var end0 = arguments[1];
106912             var mce$1 = arguments[2];
106913             var start1 = arguments[3];
106914             var end1 = arguments[4];
106915             var ei = arguments[5];
106916             var p00 = this.pts[start0];
106917             var p01 = this.pts[end0];
106918             var p10 = mce$1.pts[start1];
106919             var p11 = mce$1.pts[end1];
106920             if (end0 - start0 === 1 && end1 - start1 === 1) {
106921               ei.addIntersections(this.e, start0, mce$1.e, start1);
106922               return null
106923             }
106924             this.env1.init(p00, p01);
106925             this.env2.init(p10, p11);
106926             if (!this.env1.intersects(this.env2)) { return null }
106927             var mid0 = Math.trunc((start0 + end0) / 2);
106928             var mid1 = Math.trunc((start1 + end1) / 2);
106929             if (start0 < mid0) {
106930               if (start1 < mid1) { this.computeIntersectsForChain(start0, mid0, mce$1, start1, mid1, ei); }
106931               if (mid1 < end1) { this.computeIntersectsForChain(start0, mid0, mce$1, mid1, end1, ei); }
106932             }
106933             if (mid0 < end0) {
106934               if (start1 < mid1) { this.computeIntersectsForChain(mid0, end0, mce$1, start1, mid1, ei); }
106935               if (mid1 < end1) { this.computeIntersectsForChain(mid0, end0, mce$1, mid1, end1, ei); }
106936             }
106937           }
106938         };
106939         MonotoneChainEdge.prototype.getStartIndexes = function getStartIndexes () {
106940           return this.startIndex
106941         };
106942         MonotoneChainEdge.prototype.computeIntersects = function computeIntersects (mce, si) {
106943             var this$1 = this;
106944
106945           for (var i = 0; i < this.startIndex.length - 1; i++) {
106946             for (var j = 0; j < mce.startIndex.length - 1; j++) {
106947               this$1.computeIntersectsForChain(i, mce, j, si);
106948             }
106949           }
106950         };
106951         MonotoneChainEdge.prototype.interfaces_ = function interfaces_ () {
106952           return []
106953         };
106954         MonotoneChainEdge.prototype.getClass = function getClass () {
106955           return MonotoneChainEdge
106956         };
106957
106958         var Depth = function Depth () {
106959           var this$1 = this;
106960
106961           this._depth = Array(2).fill().map(function () { return Array(3); });
106962           for (var i = 0; i < 2; i++) {
106963             for (var j = 0; j < 3; j++) {
106964               this$1._depth[i][j] = Depth.NULL_VALUE;
106965             }
106966           }
106967         };
106968
106969         var staticAccessors$31 = { NULL_VALUE: { configurable: true } };
106970         Depth.prototype.getDepth = function getDepth (geomIndex, posIndex) {
106971           return this._depth[geomIndex][posIndex]
106972         };
106973         Depth.prototype.setDepth = function setDepth (geomIndex, posIndex, depthValue) {
106974           this._depth[geomIndex][posIndex] = depthValue;
106975         };
106976         Depth.prototype.isNull = function isNull () {
106977             var this$1 = this;
106978
106979           if (arguments.length === 0) {
106980             for (var i = 0; i < 2; i++) {
106981               for (var j = 0; j < 3; j++) {
106982                 if (this$1._depth[i][j] !== Depth.NULL_VALUE) { return false }
106983               }
106984             }
106985             return true
106986           } else if (arguments.length === 1) {
106987             var geomIndex = arguments[0];
106988             return this._depth[geomIndex][1] === Depth.NULL_VALUE
106989           } else if (arguments.length === 2) {
106990             var geomIndex$1 = arguments[0];
106991             var posIndex = arguments[1];
106992             return this._depth[geomIndex$1][posIndex] === Depth.NULL_VALUE
106993           }
106994         };
106995         Depth.prototype.normalize = function normalize () {
106996             var this$1 = this;
106997
106998           for (var i = 0; i < 2; i++) {
106999             if (!this$1.isNull(i)) {
107000               var minDepth = this$1._depth[i][1];
107001               if (this$1._depth[i][2] < minDepth) { minDepth = this$1._depth[i][2]; }
107002               if (minDepth < 0) { minDepth = 0; }
107003               for (var j = 1; j < 3; j++) {
107004                 var newValue = 0;
107005                 if (this$1._depth[i][j] > minDepth) { newValue = 1; }
107006                 this$1._depth[i][j] = newValue;
107007               }
107008             }
107009           }
107010         };
107011         Depth.prototype.getDelta = function getDelta (geomIndex) {
107012           return this._depth[geomIndex][Position.RIGHT] - this._depth[geomIndex][Position.LEFT]
107013         };
107014         Depth.prototype.getLocation = function getLocation (geomIndex, posIndex) {
107015           if (this._depth[geomIndex][posIndex] <= 0) { return Location.EXTERIOR }
107016           return Location.INTERIOR
107017         };
107018         Depth.prototype.toString = function toString () {
107019           return 'A: ' + this._depth[0][1] + ',' + this._depth[0][2] + ' B: ' + this._depth[1][1] + ',' + this._depth[1][2]
107020         };
107021         Depth.prototype.add = function add () {
107022             var this$1 = this;
107023
107024           if (arguments.length === 1) {
107025             var lbl = arguments[0];
107026             for (var i = 0; i < 2; i++) {
107027               for (var j = 1; j < 3; j++) {
107028                 var loc = lbl.getLocation(i, j);
107029                 if (loc === Location.EXTERIOR || loc === Location.INTERIOR) {
107030                   if (this$1.isNull(i, j)) {
107031                     this$1._depth[i][j] = Depth.depthAtLocation(loc);
107032                   } else { this$1._depth[i][j] += Depth.depthAtLocation(loc); }
107033                 }
107034               }
107035             }
107036           } else if (arguments.length === 3) {
107037             var geomIndex = arguments[0];
107038             var posIndex = arguments[1];
107039             var location = arguments[2];
107040             if (location === Location.INTERIOR) { this._depth[geomIndex][posIndex]++; }
107041           }
107042         };
107043         Depth.prototype.interfaces_ = function interfaces_ () {
107044           return []
107045         };
107046         Depth.prototype.getClass = function getClass () {
107047           return Depth
107048         };
107049         Depth.depthAtLocation = function depthAtLocation (location) {
107050           if (location === Location.EXTERIOR) { return 0 }
107051           if (location === Location.INTERIOR) { return 1 }
107052           return Depth.NULL_VALUE
107053         };
107054         staticAccessors$31.NULL_VALUE.get = function () { return -1 };
107055
107056         Object.defineProperties( Depth, staticAccessors$31 );
107057
107058         var Edge = (function (GraphComponent$$1) {
107059           function Edge () {
107060             GraphComponent$$1.call(this);
107061             this.pts = null;
107062             this._env = null;
107063             this.eiList = new EdgeIntersectionList(this);
107064             this._name = null;
107065             this._mce = null;
107066             this._isIsolated = true;
107067             this._depth = new Depth();
107068             this._depthDelta = 0;
107069             if (arguments.length === 1) {
107070               var pts = arguments[0];
107071               Edge.call(this, pts, null);
107072             } else if (arguments.length === 2) {
107073               var pts$1 = arguments[0];
107074               var label = arguments[1];
107075               this.pts = pts$1;
107076               this._label = label;
107077             }
107078           }
107079
107080           if ( GraphComponent$$1 ) { Edge.__proto__ = GraphComponent$$1; }
107081           Edge.prototype = Object.create( GraphComponent$$1 && GraphComponent$$1.prototype );
107082           Edge.prototype.constructor = Edge;
107083           Edge.prototype.getDepth = function getDepth () {
107084             return this._depth
107085           };
107086           Edge.prototype.getCollapsedEdge = function getCollapsedEdge () {
107087             var newPts = new Array(2).fill(null);
107088             newPts[0] = this.pts[0];
107089             newPts[1] = this.pts[1];
107090             var newe = new Edge(newPts, Label.toLineLabel(this._label));
107091             return newe
107092           };
107093           Edge.prototype.isIsolated = function isIsolated () {
107094             return this._isIsolated
107095           };
107096           Edge.prototype.getCoordinates = function getCoordinates () {
107097             return this.pts
107098           };
107099           Edge.prototype.setIsolated = function setIsolated (isIsolated) {
107100             this._isIsolated = isIsolated;
107101           };
107102           Edge.prototype.setName = function setName (name) {
107103             this._name = name;
107104           };
107105           Edge.prototype.equals = function equals (o) {
107106             var this$1 = this;
107107
107108             if (!(o instanceof Edge)) { return false }
107109             var e = o;
107110             if (this.pts.length !== e.pts.length) { return false }
107111             var isEqualForward = true;
107112             var isEqualReverse = true;
107113             var iRev = this.pts.length;
107114             for (var i = 0; i < this.pts.length; i++) {
107115               if (!this$1.pts[i].equals2D(e.pts[i])) {
107116                 isEqualForward = false;
107117               }
107118               if (!this$1.pts[i].equals2D(e.pts[--iRev])) {
107119                 isEqualReverse = false;
107120               }
107121               if (!isEqualForward && !isEqualReverse) { return false }
107122             }
107123             return true
107124           };
107125           Edge.prototype.getCoordinate = function getCoordinate () {
107126             if (arguments.length === 0) {
107127               if (this.pts.length > 0) { return this.pts[0] }
107128               return null
107129             } else if (arguments.length === 1) {
107130               var i = arguments[0];
107131               return this.pts[i]
107132             }
107133           };
107134           Edge.prototype.print = function print (out) {
107135             var this$1 = this;
107136
107137             out.print('edge ' + this._name + ': ');
107138             out.print('LINESTRING (');
107139             for (var i = 0; i < this.pts.length; i++) {
107140               if (i > 0) { out.print(','); }
107141               out.print(this$1.pts[i].x + ' ' + this$1.pts[i].y);
107142             }
107143             out.print(')  ' + this._label + ' ' + this._depthDelta);
107144           };
107145           Edge.prototype.computeIM = function computeIM (im) {
107146             Edge.updateIM(this._label, im);
107147           };
107148           Edge.prototype.isCollapsed = function isCollapsed () {
107149             if (!this._label.isArea()) { return false }
107150             if (this.pts.length !== 3) { return false }
107151             if (this.pts[0].equals(this.pts[2])) { return true }
107152             return false
107153           };
107154           Edge.prototype.isClosed = function isClosed () {
107155             return this.pts[0].equals(this.pts[this.pts.length - 1])
107156           };
107157           Edge.prototype.getMaximumSegmentIndex = function getMaximumSegmentIndex () {
107158             return this.pts.length - 1
107159           };
107160           Edge.prototype.getDepthDelta = function getDepthDelta () {
107161             return this._depthDelta
107162           };
107163           Edge.prototype.getNumPoints = function getNumPoints () {
107164             return this.pts.length
107165           };
107166           Edge.prototype.printReverse = function printReverse (out) {
107167             var this$1 = this;
107168
107169             out.print('edge ' + this._name + ': ');
107170             for (var i = this.pts.length - 1; i >= 0; i--) {
107171               out.print(this$1.pts[i] + ' ');
107172             }
107173             out.println('');
107174           };
107175           Edge.prototype.getMonotoneChainEdge = function getMonotoneChainEdge () {
107176             if (this._mce === null) { this._mce = new MonotoneChainEdge(this); }
107177             return this._mce
107178           };
107179           Edge.prototype.getEnvelope = function getEnvelope () {
107180             var this$1 = this;
107181
107182             if (this._env === null) {
107183               this._env = new Envelope();
107184               for (var i = 0; i < this.pts.length; i++) {
107185                 this$1._env.expandToInclude(this$1.pts[i]);
107186               }
107187             }
107188             return this._env
107189           };
107190           Edge.prototype.addIntersection = function addIntersection (li, segmentIndex, geomIndex, intIndex) {
107191             var intPt = new Coordinate(li.getIntersection(intIndex));
107192             var normalizedSegmentIndex = segmentIndex;
107193             var dist = li.getEdgeDistance(geomIndex, intIndex);
107194             var nextSegIndex = normalizedSegmentIndex + 1;
107195             if (nextSegIndex < this.pts.length) {
107196               var nextPt = this.pts[nextSegIndex];
107197               if (intPt.equals2D(nextPt)) {
107198                 normalizedSegmentIndex = nextSegIndex;
107199                 dist = 0.0;
107200               }
107201             }
107202             this.eiList.add(intPt, normalizedSegmentIndex, dist);
107203           };
107204           Edge.prototype.toString = function toString () {
107205             var this$1 = this;
107206
107207             var buf = new StringBuffer();
107208             buf.append('edge ' + this._name + ': ');
107209             buf.append('LINESTRING (');
107210             for (var i = 0; i < this.pts.length; i++) {
107211               if (i > 0) { buf.append(','); }
107212               buf.append(this$1.pts[i].x + ' ' + this$1.pts[i].y);
107213             }
107214             buf.append(')  ' + this._label + ' ' + this._depthDelta);
107215             return buf.toString()
107216           };
107217           Edge.prototype.isPointwiseEqual = function isPointwiseEqual (e) {
107218             var this$1 = this;
107219
107220             if (this.pts.length !== e.pts.length) { return false }
107221             for (var i = 0; i < this.pts.length; i++) {
107222               if (!this$1.pts[i].equals2D(e.pts[i])) {
107223                 return false
107224               }
107225             }
107226             return true
107227           };
107228           Edge.prototype.setDepthDelta = function setDepthDelta (depthDelta) {
107229             this._depthDelta = depthDelta;
107230           };
107231           Edge.prototype.getEdgeIntersectionList = function getEdgeIntersectionList () {
107232             return this.eiList
107233           };
107234           Edge.prototype.addIntersections = function addIntersections (li, segmentIndex, geomIndex) {
107235             var this$1 = this;
107236
107237             for (var i = 0; i < li.getIntersectionNum(); i++) {
107238               this$1.addIntersection(li, segmentIndex, geomIndex, i);
107239             }
107240           };
107241           Edge.prototype.interfaces_ = function interfaces_ () {
107242             return []
107243           };
107244           Edge.prototype.getClass = function getClass () {
107245             return Edge
107246           };
107247           Edge.updateIM = function updateIM () {
107248             if (arguments.length === 2) {
107249               var label = arguments[0];
107250               var im = arguments[1];
107251               im.setAtLeastIfValid(label.getLocation(0, Position.ON), label.getLocation(1, Position.ON), 1);
107252               if (label.isArea()) {
107253                 im.setAtLeastIfValid(label.getLocation(0, Position.LEFT), label.getLocation(1, Position.LEFT), 2);
107254                 im.setAtLeastIfValid(label.getLocation(0, Position.RIGHT), label.getLocation(1, Position.RIGHT), 2);
107255               }
107256             } else { return GraphComponent$$1.prototype.updateIM.apply(this, arguments) }
107257           };
107258
107259           return Edge;
107260         }(GraphComponent));
107261
107262         var BufferBuilder = function BufferBuilder (bufParams) {
107263           this._workingPrecisionModel = null;
107264           this._workingNoder = null;
107265           this._geomFact = null;
107266           this._graph = null;
107267           this._edgeList = new EdgeList();
107268           this._bufParams = bufParams || null;
107269         };
107270         BufferBuilder.prototype.setWorkingPrecisionModel = function setWorkingPrecisionModel (pm) {
107271           this._workingPrecisionModel = pm;
107272         };
107273         BufferBuilder.prototype.insertUniqueEdge = function insertUniqueEdge (e) {
107274           var existingEdge = this._edgeList.findEqualEdge(e);
107275           if (existingEdge !== null) {
107276             var existingLabel = existingEdge.getLabel();
107277             var labelToMerge = e.getLabel();
107278             if (!existingEdge.isPointwiseEqual(e)) {
107279               labelToMerge = new Label(e.getLabel());
107280               labelToMerge.flip();
107281             }
107282             existingLabel.merge(labelToMerge);
107283             var mergeDelta = BufferBuilder.depthDelta(labelToMerge);
107284             var existingDelta = existingEdge.getDepthDelta();
107285             var newDelta = existingDelta + mergeDelta;
107286             existingEdge.setDepthDelta(newDelta);
107287           } else {
107288             this._edgeList.add(e);
107289             e.setDepthDelta(BufferBuilder.depthDelta(e.getLabel()));
107290           }
107291         };
107292         BufferBuilder.prototype.buildSubgraphs = function buildSubgraphs (subgraphList, polyBuilder) {
107293           var processedGraphs = new ArrayList();
107294           for (var i = subgraphList.iterator(); i.hasNext();) {
107295             var subgraph = i.next();
107296             var p = subgraph.getRightmostCoordinate();
107297             var locater = new SubgraphDepthLocater(processedGraphs);
107298             var outsideDepth = locater.getDepth(p);
107299             subgraph.computeDepth(outsideDepth);
107300             subgraph.findResultEdges();
107301             processedGraphs.add(subgraph);
107302             polyBuilder.add(subgraph.getDirectedEdges(), subgraph.getNodes());
107303           }
107304         };
107305         BufferBuilder.prototype.createSubgraphs = function createSubgraphs (graph) {
107306           var subgraphList = new ArrayList();
107307           for (var i = graph.getNodes().iterator(); i.hasNext();) {
107308             var node = i.next();
107309             if (!node.isVisited()) {
107310               var subgraph = new BufferSubgraph();
107311               subgraph.create(node);
107312               subgraphList.add(subgraph);
107313             }
107314           }
107315           Collections.sort(subgraphList, Collections.reverseOrder());
107316           return subgraphList
107317         };
107318         BufferBuilder.prototype.createEmptyResultGeometry = function createEmptyResultGeometry () {
107319           var emptyGeom = this._geomFact.createPolygon();
107320           return emptyGeom
107321         };
107322         BufferBuilder.prototype.getNoder = function getNoder (precisionModel) {
107323           if (this._workingNoder !== null) { return this._workingNoder }
107324           var noder = new MCIndexNoder();
107325           var li = new RobustLineIntersector();
107326           li.setPrecisionModel(precisionModel);
107327           noder.setSegmentIntersector(new IntersectionAdder(li));
107328           return noder
107329         };
107330         BufferBuilder.prototype.buffer = function buffer (g, distance) {
107331           var precisionModel = this._workingPrecisionModel;
107332           if (precisionModel === null) { precisionModel = g.getPrecisionModel(); }
107333           this._geomFact = g.getFactory();
107334           var curveBuilder = new OffsetCurveBuilder(precisionModel, this._bufParams);
107335           var curveSetBuilder = new OffsetCurveSetBuilder(g, distance, curveBuilder);
107336           var bufferSegStrList = curveSetBuilder.getCurves();
107337           if (bufferSegStrList.size() <= 0) {
107338             return this.createEmptyResultGeometry()
107339           }
107340           this.computeNodedEdges(bufferSegStrList, precisionModel);
107341           this._graph = new PlanarGraph(new OverlayNodeFactory());
107342           this._graph.addEdges(this._edgeList.getEdges());
107343           var subgraphList = this.createSubgraphs(this._graph);
107344           var polyBuilder = new PolygonBuilder(this._geomFact);
107345           this.buildSubgraphs(subgraphList, polyBuilder);
107346           var resultPolyList = polyBuilder.getPolygons();
107347           if (resultPolyList.size() <= 0) {
107348             return this.createEmptyResultGeometry()
107349           }
107350           var resultGeom = this._geomFact.buildGeometry(resultPolyList);
107351           return resultGeom
107352         };
107353         BufferBuilder.prototype.computeNodedEdges = function computeNodedEdges (bufferSegStrList, precisionModel) {
107354             var this$1 = this;
107355
107356           var noder = this.getNoder(precisionModel);
107357           noder.computeNodes(bufferSegStrList);
107358           var nodedSegStrings = noder.getNodedSubstrings();
107359           for (var i = nodedSegStrings.iterator(); i.hasNext();) {
107360             var segStr = i.next();
107361             var pts = segStr.getCoordinates();
107362             if (pts.length === 2 && pts[0].equals2D(pts[1])) { continue }
107363             var oldLabel = segStr.getData();
107364             var edge = new Edge(segStr.getCoordinates(), new Label(oldLabel));
107365             this$1.insertUniqueEdge(edge);
107366           }
107367         };
107368         BufferBuilder.prototype.setNoder = function setNoder (noder) {
107369           this._workingNoder = noder;
107370         };
107371         BufferBuilder.prototype.interfaces_ = function interfaces_ () {
107372           return []
107373         };
107374         BufferBuilder.prototype.getClass = function getClass () {
107375           return BufferBuilder
107376         };
107377         BufferBuilder.depthDelta = function depthDelta (label) {
107378           var lLoc = label.getLocation(0, Position.LEFT);
107379           var rLoc = label.getLocation(0, Position.RIGHT);
107380           if (lLoc === Location.INTERIOR && rLoc === Location.EXTERIOR) { return 1; } else if (lLoc === Location.EXTERIOR && rLoc === Location.INTERIOR) { return -1 }
107381           return 0
107382         };
107383         BufferBuilder.convertSegStrings = function convertSegStrings (it) {
107384           var fact = new GeometryFactory();
107385           var lines = new ArrayList();
107386           while (it.hasNext()) {
107387             var ss = it.next();
107388             var line = fact.createLineString(ss.getCoordinates());
107389             lines.add(line);
107390           }
107391           return fact.buildGeometry(lines)
107392         };
107393
107394         var ScaledNoder = function ScaledNoder () {
107395           this._noder = null;
107396           this._scaleFactor = null;
107397           this._offsetX = null;
107398           this._offsetY = null;
107399           this._isScaled = false;
107400           if (arguments.length === 2) {
107401             var noder = arguments[0];
107402             var scaleFactor = arguments[1];
107403             this._noder = noder;
107404             this._scaleFactor = scaleFactor;
107405             this._offsetX = 0.0;
107406             this._offsetY = 0.0;
107407             this._isScaled = !this.isIntegerPrecision();
107408           } else if (arguments.length === 4) {
107409             var noder$1 = arguments[0];
107410             var scaleFactor$1 = arguments[1];
107411             var offsetX = arguments[2];
107412             var offsetY = arguments[3];
107413             this._noder = noder$1;
107414             this._scaleFactor = scaleFactor$1;
107415             this._offsetX = offsetX;
107416             this._offsetY = offsetY;
107417             this._isScaled = !this.isIntegerPrecision();
107418           }
107419         };
107420         ScaledNoder.prototype.rescale = function rescale () {
107421             var this$1 = this;
107422
107423           if (hasInterface(arguments[0], Collection)) {
107424             var segStrings = arguments[0];
107425             for (var i = segStrings.iterator(); i.hasNext();) {
107426               var ss = i.next();
107427               this$1.rescale(ss.getCoordinates());
107428             }
107429           } else if (arguments[0] instanceof Array) {
107430             var pts = arguments[0];
107431             // let p0 = null
107432             // let p1 = null
107433             // if (pts.length === 2) {
107434             // p0 = new Coordinate(pts[0])
107435             // p1 = new Coordinate(pts[1])
107436             // }
107437             for (var i$1 = 0; i$1 < pts.length; i$1++) {
107438               pts[i$1].x = pts[i$1].x / this$1._scaleFactor + this$1._offsetX;
107439               pts[i$1].y = pts[i$1].y / this$1._scaleFactor + this$1._offsetY;
107440             }
107441             if (pts.length === 2 && pts[0].equals2D(pts[1])) {
107442               System.out.println(pts);
107443             }
107444           }
107445         };
107446         ScaledNoder.prototype.scale = function scale () {
107447             var this$1 = this;
107448
107449           if (hasInterface(arguments[0], Collection)) {
107450             var segStrings = arguments[0];
107451             var nodedSegmentStrings = new ArrayList();
107452             for (var i = segStrings.iterator(); i.hasNext();) {
107453               var ss = i.next();
107454               nodedSegmentStrings.add(new NodedSegmentString(this$1.scale(ss.getCoordinates()), ss.getData()));
107455             }
107456             return nodedSegmentStrings
107457           } else if (arguments[0] instanceof Array) {
107458             var pts = arguments[0];
107459             var roundPts = new Array(pts.length).fill(null);
107460             for (var i$1 = 0; i$1 < pts.length; i$1++) {
107461               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);
107462             }
107463             var roundPtsNoDup = CoordinateArrays.removeRepeatedPoints(roundPts);
107464             return roundPtsNoDup
107465           }
107466         };
107467         ScaledNoder.prototype.isIntegerPrecision = function isIntegerPrecision () {
107468           return this._scaleFactor === 1.0
107469         };
107470         ScaledNoder.prototype.getNodedSubstrings = function getNodedSubstrings () {
107471           var splitSS = this._noder.getNodedSubstrings();
107472           if (this._isScaled) { this.rescale(splitSS); }
107473           return splitSS
107474         };
107475         ScaledNoder.prototype.computeNodes = function computeNodes (inputSegStrings) {
107476           var intSegStrings = inputSegStrings;
107477           if (this._isScaled) { intSegStrings = this.scale(inputSegStrings); }
107478           this._noder.computeNodes(intSegStrings);
107479         };
107480         ScaledNoder.prototype.interfaces_ = function interfaces_ () {
107481           return [Noder]
107482         };
107483         ScaledNoder.prototype.getClass = function getClass () {
107484           return ScaledNoder
107485         };
107486
107487         var NodingValidator = function NodingValidator () {
107488           this._li = new RobustLineIntersector();
107489           this._segStrings = null;
107490           var segStrings = arguments[0];
107491           this._segStrings = segStrings;
107492         };
107493
107494         var staticAccessors$33 = { fact: { configurable: true } };
107495         NodingValidator.prototype.checkEndPtVertexIntersections = function checkEndPtVertexIntersections () {
107496             var this$1 = this;
107497
107498           if (arguments.length === 0) {
107499             for (var i = this._segStrings.iterator(); i.hasNext();) {
107500               var ss = i.next();
107501               var pts = ss.getCoordinates();
107502               this$1.checkEndPtVertexIntersections(pts[0], this$1._segStrings);
107503               this$1.checkEndPtVertexIntersections(pts[pts.length - 1], this$1._segStrings);
107504             }
107505           } else if (arguments.length === 2) {
107506             var testPt = arguments[0];
107507             var segStrings = arguments[1];
107508             for (var i$1 = segStrings.iterator(); i$1.hasNext();) {
107509               var ss$1 = i$1.next();
107510               var pts$1 = ss$1.getCoordinates();
107511               for (var j = 1; j < pts$1.length - 1; j++) {
107512                 if (pts$1[j].equals(testPt)) { throw new RuntimeException('found endpt/interior pt intersection at index ' + j + ' :pt ' + testPt) }
107513               }
107514             }
107515           }
107516         };
107517         NodingValidator.prototype.checkInteriorIntersections = function checkInteriorIntersections () {
107518             var this$1 = this;
107519
107520           if (arguments.length === 0) {
107521             for (var i = this._segStrings.iterator(); i.hasNext();) {
107522               var ss0 = i.next();
107523               for (var j = this._segStrings.iterator(); j.hasNext();) {
107524                 var ss1 = j.next();
107525                 this$1.checkInteriorIntersections(ss0, ss1);
107526               }
107527             }
107528           } else if (arguments.length === 2) {
107529             var ss0$1 = arguments[0];
107530             var ss1$1 = arguments[1];
107531             var pts0 = ss0$1.getCoordinates();
107532             var pts1 = ss1$1.getCoordinates();
107533             for (var i0 = 0; i0 < pts0.length - 1; i0++) {
107534               for (var i1 = 0; i1 < pts1.length - 1; i1++) {
107535                 this$1.checkInteriorIntersections(ss0$1, i0, ss1$1, i1);
107536               }
107537             }
107538           } else if (arguments.length === 4) {
107539             var e0 = arguments[0];
107540             var segIndex0 = arguments[1];
107541             var e1 = arguments[2];
107542             var segIndex1 = arguments[3];
107543             if (e0 === e1 && segIndex0 === segIndex1) { return null }
107544             var p00 = e0.getCoordinates()[segIndex0];
107545             var p01 = e0.getCoordinates()[segIndex0 + 1];
107546             var p10 = e1.getCoordinates()[segIndex1];
107547             var p11 = e1.getCoordinates()[segIndex1 + 1];
107548             this._li.computeIntersection(p00, p01, p10, p11);
107549             if (this._li.hasIntersection()) {
107550               if (this._li.isProper() || this.hasInteriorIntersection(this._li, p00, p01) || this.hasInteriorIntersection(this._li, p10, p11)) {
107551                 throw new RuntimeException('found non-noded intersection at ' + p00 + '-' + p01 + ' and ' + p10 + '-' + p11)
107552               }
107553             }
107554           }
107555         };
107556         NodingValidator.prototype.checkValid = function checkValid () {
107557           this.checkEndPtVertexIntersections();
107558           this.checkInteriorIntersections();
107559           this.checkCollapses();
107560         };
107561         NodingValidator.prototype.checkCollapses = function checkCollapses () {
107562             var this$1 = this;
107563
107564           if (arguments.length === 0) {
107565             for (var i = this._segStrings.iterator(); i.hasNext();) {
107566               var ss = i.next();
107567               this$1.checkCollapses(ss);
107568             }
107569           } else if (arguments.length === 1) {
107570             var ss$1 = arguments[0];
107571             var pts = ss$1.getCoordinates();
107572             for (var i$1 = 0; i$1 < pts.length - 2; i$1++) {
107573               this$1.checkCollapse(pts[i$1], pts[i$1 + 1], pts[i$1 + 2]);
107574             }
107575           }
107576         };
107577         NodingValidator.prototype.hasInteriorIntersection = function hasInteriorIntersection (li, p0, p1) {
107578           for (var i = 0; i < li.getIntersectionNum(); i++) {
107579             var intPt = li.getIntersection(i);
107580             if (!(intPt.equals(p0) || intPt.equals(p1))) { return true }
107581           }
107582           return false
107583         };
107584         NodingValidator.prototype.checkCollapse = function checkCollapse (p0, p1, p2) {
107585           if (p0.equals(p2)) { throw new RuntimeException('found non-noded collapse at ' + NodingValidator.fact.createLineString([p0, p1, p2])) }
107586         };
107587         NodingValidator.prototype.interfaces_ = function interfaces_ () {
107588           return []
107589         };
107590         NodingValidator.prototype.getClass = function getClass () {
107591           return NodingValidator
107592         };
107593         staticAccessors$33.fact.get = function () { return new GeometryFactory() };
107594
107595         Object.defineProperties( NodingValidator, staticAccessors$33 );
107596
107597         var HotPixel = function HotPixel () {
107598           this._li = null;
107599           this._pt = null;
107600           this._originalPt = null;
107601           this._ptScaled = null;
107602           this._p0Scaled = null;
107603           this._p1Scaled = null;
107604           this._scaleFactor = null;
107605           this._minx = null;
107606           this._maxx = null;
107607           this._miny = null;
107608           this._maxy = null;
107609           this._corner = new Array(4).fill(null);
107610           this._safeEnv = null;
107611           var pt = arguments[0];
107612           var scaleFactor = arguments[1];
107613           var li = arguments[2];
107614           this._originalPt = pt;
107615           this._pt = pt;
107616           this._scaleFactor = scaleFactor;
107617           this._li = li;
107618           if (scaleFactor <= 0) { throw new IllegalArgumentException('Scale factor must be non-zero') }
107619           if (scaleFactor !== 1.0) {
107620             this._pt = new Coordinate(this.scale(pt.x), this.scale(pt.y));
107621             this._p0Scaled = new Coordinate();
107622             this._p1Scaled = new Coordinate();
107623           }
107624           this.initCorners(this._pt);
107625         };
107626
107627         var staticAccessors$34 = { SAFE_ENV_EXPANSION_FACTOR: { configurable: true } };
107628         HotPixel.prototype.intersectsScaled = function intersectsScaled (p0, p1) {
107629           var segMinx = Math.min(p0.x, p1.x);
107630           var segMaxx = Math.max(p0.x, p1.x);
107631           var segMiny = Math.min(p0.y, p1.y);
107632           var segMaxy = Math.max(p0.y, p1.y);
107633           var isOutsidePixelEnv = this._maxx < segMinx || this._minx > segMaxx || this._maxy < segMiny || this._miny > segMaxy;
107634           if (isOutsidePixelEnv) { return false }
107635           var intersects = this.intersectsToleranceSquare(p0, p1);
107636           Assert.isTrue(!(isOutsidePixelEnv && intersects), 'Found bad envelope test');
107637           return intersects
107638         };
107639         HotPixel.prototype.initCorners = function initCorners (pt) {
107640           var tolerance = 0.5;
107641           this._minx = pt.x - tolerance;
107642           this._maxx = pt.x + tolerance;
107643           this._miny = pt.y - tolerance;
107644           this._maxy = pt.y + tolerance;
107645           this._corner[0] = new Coordinate(this._maxx, this._maxy);
107646           this._corner[1] = new Coordinate(this._minx, this._maxy);
107647           this._corner[2] = new Coordinate(this._minx, this._miny);
107648           this._corner[3] = new Coordinate(this._maxx, this._miny);
107649         };
107650         HotPixel.prototype.intersects = function intersects (p0, p1) {
107651           if (this._scaleFactor === 1.0) { return this.intersectsScaled(p0, p1) }
107652           this.copyScaled(p0, this._p0Scaled);
107653           this.copyScaled(p1, this._p1Scaled);
107654           return this.intersectsScaled(this._p0Scaled, this._p1Scaled)
107655         };
107656         HotPixel.prototype.scale = function scale (val) {
107657           return Math.round(val * this._scaleFactor)
107658         };
107659         HotPixel.prototype.getCoordinate = function getCoordinate () {
107660           return this._originalPt
107661         };
107662         HotPixel.prototype.copyScaled = function copyScaled (p, pScaled) {
107663           pScaled.x = this.scale(p.x);
107664           pScaled.y = this.scale(p.y);
107665         };
107666         HotPixel.prototype.getSafeEnvelope = function getSafeEnvelope () {
107667           if (this._safeEnv === null) {
107668             var safeTolerance = HotPixel.SAFE_ENV_EXPANSION_FACTOR / this._scaleFactor;
107669             this._safeEnv = new Envelope(this._originalPt.x - safeTolerance, this._originalPt.x + safeTolerance, this._originalPt.y - safeTolerance, this._originalPt.y + safeTolerance);
107670           }
107671           return this._safeEnv
107672         };
107673         HotPixel.prototype.intersectsPixelClosure = function intersectsPixelClosure (p0, p1) {
107674           this._li.computeIntersection(p0, p1, this._corner[0], this._corner[1]);
107675           if (this._li.hasIntersection()) { return true }
107676           this._li.computeIntersection(p0, p1, this._corner[1], this._corner[2]);
107677           if (this._li.hasIntersection()) { return true }
107678           this._li.computeIntersection(p0, p1, this._corner[2], this._corner[3]);
107679           if (this._li.hasIntersection()) { return true }
107680           this._li.computeIntersection(p0, p1, this._corner[3], this._corner[0]);
107681           if (this._li.hasIntersection()) { return true }
107682           return false
107683         };
107684         HotPixel.prototype.intersectsToleranceSquare = function intersectsToleranceSquare (p0, p1) {
107685           var intersectsLeft = false;
107686           var intersectsBottom = false;
107687           this._li.computeIntersection(p0, p1, this._corner[0], this._corner[1]);
107688           if (this._li.isProper()) { return true }
107689           this._li.computeIntersection(p0, p1, this._corner[1], this._corner[2]);
107690           if (this._li.isProper()) { return true }
107691           if (this._li.hasIntersection()) { intersectsLeft = true; }
107692           this._li.computeIntersection(p0, p1, this._corner[2], this._corner[3]);
107693           if (this._li.isProper()) { return true }
107694           if (this._li.hasIntersection()) { intersectsBottom = true; }
107695           this._li.computeIntersection(p0, p1, this._corner[3], this._corner[0]);
107696           if (this._li.isProper()) { return true }
107697           if (intersectsLeft && intersectsBottom) { return true }
107698           if (p0.equals(this._pt)) { return true }
107699           if (p1.equals(this._pt)) { return true }
107700           return false
107701         };
107702         HotPixel.prototype.addSnappedNode = function addSnappedNode (segStr, segIndex) {
107703           var p0 = segStr.getCoordinate(segIndex);
107704           var p1 = segStr.getCoordinate(segIndex + 1);
107705           if (this.intersects(p0, p1)) {
107706             segStr.addIntersection(this.getCoordinate(), segIndex);
107707             return true
107708           }
107709           return false
107710         };
107711         HotPixel.prototype.interfaces_ = function interfaces_ () {
107712           return []
107713         };
107714         HotPixel.prototype.getClass = function getClass () {
107715           return HotPixel
107716         };
107717         staticAccessors$34.SAFE_ENV_EXPANSION_FACTOR.get = function () { return 0.75 };
107718
107719         Object.defineProperties( HotPixel, staticAccessors$34 );
107720
107721         var MonotoneChainSelectAction = function MonotoneChainSelectAction () {
107722           this.tempEnv1 = new Envelope();
107723           this.selectedSegment = new LineSegment();
107724         };
107725         MonotoneChainSelectAction.prototype.select = function select () {
107726           if (arguments.length === 1) ; else if (arguments.length === 2) {
107727             var mc = arguments[0];
107728             var startIndex = arguments[1];
107729             mc.getLineSegment(startIndex, this.selectedSegment);
107730             this.select(this.selectedSegment);
107731           }
107732         };
107733         MonotoneChainSelectAction.prototype.interfaces_ = function interfaces_ () {
107734           return []
107735         };
107736         MonotoneChainSelectAction.prototype.getClass = function getClass () {
107737           return MonotoneChainSelectAction
107738         };
107739
107740         var MCIndexPointSnapper = function MCIndexPointSnapper () {
107741           this._index = null;
107742           var index = arguments[0];
107743           this._index = index;
107744         };
107745
107746         var staticAccessors$35 = { HotPixelSnapAction: { configurable: true } };
107747         MCIndexPointSnapper.prototype.snap = function snap () {
107748           if (arguments.length === 1) {
107749             var hotPixel = arguments[0];
107750             return this.snap(hotPixel, null, -1)
107751           } else if (arguments.length === 3) {
107752             var hotPixel$1 = arguments[0];
107753             var parentEdge = arguments[1];
107754             var hotPixelVertexIndex = arguments[2];
107755             var pixelEnv = hotPixel$1.getSafeEnvelope();
107756             var hotPixelSnapAction = new HotPixelSnapAction(hotPixel$1, parentEdge, hotPixelVertexIndex);
107757             this._index.query(pixelEnv, {
107758               interfaces_: function () {
107759                 return [ItemVisitor]
107760               },
107761               visitItem: function (item) {
107762                 var testChain = item;
107763                 testChain.select(pixelEnv, hotPixelSnapAction);
107764               }
107765             });
107766             return hotPixelSnapAction.isNodeAdded()
107767           }
107768         };
107769         MCIndexPointSnapper.prototype.interfaces_ = function interfaces_ () {
107770           return []
107771         };
107772         MCIndexPointSnapper.prototype.getClass = function getClass () {
107773           return MCIndexPointSnapper
107774         };
107775         staticAccessors$35.HotPixelSnapAction.get = function () { return HotPixelSnapAction };
107776
107777         Object.defineProperties( MCIndexPointSnapper, staticAccessors$35 );
107778
107779         var HotPixelSnapAction = (function (MonotoneChainSelectAction$$1) {
107780           function HotPixelSnapAction () {
107781             MonotoneChainSelectAction$$1.call(this);
107782             this._hotPixel = null;
107783             this._parentEdge = null;
107784             this._hotPixelVertexIndex = null;
107785             this._isNodeAdded = false;
107786             var hotPixel = arguments[0];
107787             var parentEdge = arguments[1];
107788             var hotPixelVertexIndex = arguments[2];
107789             this._hotPixel = hotPixel;
107790             this._parentEdge = parentEdge;
107791             this._hotPixelVertexIndex = hotPixelVertexIndex;
107792           }
107793
107794           if ( MonotoneChainSelectAction$$1 ) { HotPixelSnapAction.__proto__ = MonotoneChainSelectAction$$1; }
107795           HotPixelSnapAction.prototype = Object.create( MonotoneChainSelectAction$$1 && MonotoneChainSelectAction$$1.prototype );
107796           HotPixelSnapAction.prototype.constructor = HotPixelSnapAction;
107797           HotPixelSnapAction.prototype.isNodeAdded = function isNodeAdded () {
107798             return this._isNodeAdded
107799           };
107800           HotPixelSnapAction.prototype.select = function select () {
107801             if (arguments.length === 2) {
107802               var mc = arguments[0];
107803               var startIndex = arguments[1];
107804               var ss = mc.getContext();
107805               if (this._parentEdge !== null) {
107806                 if (ss === this._parentEdge && startIndex === this._hotPixelVertexIndex) { return null }
107807               }
107808               this._isNodeAdded = this._hotPixel.addSnappedNode(ss, startIndex);
107809             } else { return MonotoneChainSelectAction$$1.prototype.select.apply(this, arguments) }
107810           };
107811           HotPixelSnapAction.prototype.interfaces_ = function interfaces_ () {
107812             return []
107813           };
107814           HotPixelSnapAction.prototype.getClass = function getClass () {
107815             return HotPixelSnapAction
107816           };
107817
107818           return HotPixelSnapAction;
107819         }(MonotoneChainSelectAction));
107820
107821         var InteriorIntersectionFinderAdder = function InteriorIntersectionFinderAdder () {
107822           this._li = null;
107823           this._interiorIntersections = null;
107824           var li = arguments[0];
107825           this._li = li;
107826           this._interiorIntersections = new ArrayList();
107827         };
107828         InteriorIntersectionFinderAdder.prototype.processIntersections = function processIntersections (e0, segIndex0, e1, segIndex1) {
107829             var this$1 = this;
107830
107831           if (e0 === e1 && segIndex0 === segIndex1) { return null }
107832           var p00 = e0.getCoordinates()[segIndex0];
107833           var p01 = e0.getCoordinates()[segIndex0 + 1];
107834           var p10 = e1.getCoordinates()[segIndex1];
107835           var p11 = e1.getCoordinates()[segIndex1 + 1];
107836           this._li.computeIntersection(p00, p01, p10, p11);
107837           if (this._li.hasIntersection()) {
107838             if (this._li.isInteriorIntersection()) {
107839               for (var intIndex = 0; intIndex < this._li.getIntersectionNum(); intIndex++) {
107840                 this$1._interiorIntersections.add(this$1._li.getIntersection(intIndex));
107841               }
107842               e0.addIntersections(this._li, segIndex0, 0);
107843               e1.addIntersections(this._li, segIndex1, 1);
107844             }
107845           }
107846         };
107847         InteriorIntersectionFinderAdder.prototype.isDone = function isDone () {
107848           return false
107849         };
107850         InteriorIntersectionFinderAdder.prototype.getInteriorIntersections = function getInteriorIntersections () {
107851           return this._interiorIntersections
107852         };
107853         InteriorIntersectionFinderAdder.prototype.interfaces_ = function interfaces_ () {
107854           return [SegmentIntersector]
107855         };
107856         InteriorIntersectionFinderAdder.prototype.getClass = function getClass () {
107857           return InteriorIntersectionFinderAdder
107858         };
107859
107860         var MCIndexSnapRounder = function MCIndexSnapRounder () {
107861           this._pm = null;
107862           this._li = null;
107863           this._scaleFactor = null;
107864           this._noder = null;
107865           this._pointSnapper = null;
107866           this._nodedSegStrings = null;
107867           var pm = arguments[0];
107868           this._pm = pm;
107869           this._li = new RobustLineIntersector();
107870           this._li.setPrecisionModel(pm);
107871           this._scaleFactor = pm.getScale();
107872         };
107873         MCIndexSnapRounder.prototype.checkCorrectness = function checkCorrectness (inputSegmentStrings) {
107874           var resultSegStrings = NodedSegmentString.getNodedSubstrings(inputSegmentStrings);
107875           var nv = new NodingValidator(resultSegStrings);
107876           try {
107877             nv.checkValid();
107878           } catch (ex) {
107879             if (ex instanceof Exception) {
107880               ex.printStackTrace();
107881             } else { throw ex }
107882           } finally {}
107883         };
107884         MCIndexSnapRounder.prototype.getNodedSubstrings = function getNodedSubstrings () {
107885           return NodedSegmentString.getNodedSubstrings(this._nodedSegStrings)
107886         };
107887         MCIndexSnapRounder.prototype.snapRound = function snapRound (segStrings, li) {
107888           var intersections = this.findInteriorIntersections(segStrings, li);
107889           this.computeIntersectionSnaps(intersections);
107890           this.computeVertexSnaps(segStrings);
107891         };
107892         MCIndexSnapRounder.prototype.findInteriorIntersections = function findInteriorIntersections (segStrings, li) {
107893           var intFinderAdder = new InteriorIntersectionFinderAdder(li);
107894           this._noder.setSegmentIntersector(intFinderAdder);
107895           this._noder.computeNodes(segStrings);
107896           return intFinderAdder.getInteriorIntersections()
107897         };
107898         MCIndexSnapRounder.prototype.computeVertexSnaps = function computeVertexSnaps () {
107899             var this$1 = this;
107900
107901           if (hasInterface(arguments[0], Collection)) {
107902             var edges = arguments[0];
107903             for (var i0 = edges.iterator(); i0.hasNext();) {
107904               var edge0 = i0.next();
107905               this$1.computeVertexSnaps(edge0);
107906             }
107907           } else if (arguments[0] instanceof NodedSegmentString) {
107908             var e = arguments[0];
107909             var pts0 = e.getCoordinates();
107910             for (var i = 0; i < pts0.length; i++) {
107911               var hotPixel = new HotPixel(pts0[i], this$1._scaleFactor, this$1._li);
107912               var isNodeAdded = this$1._pointSnapper.snap(hotPixel, e, i);
107913               if (isNodeAdded) {
107914                 e.addIntersection(pts0[i], i);
107915               }
107916             }
107917           }
107918         };
107919         MCIndexSnapRounder.prototype.computeNodes = function computeNodes (inputSegmentStrings) {
107920           this._nodedSegStrings = inputSegmentStrings;
107921           this._noder = new MCIndexNoder();
107922           this._pointSnapper = new MCIndexPointSnapper(this._noder.getIndex());
107923           this.snapRound(inputSegmentStrings, this._li);
107924         };
107925         MCIndexSnapRounder.prototype.computeIntersectionSnaps = function computeIntersectionSnaps (snapPts) {
107926             var this$1 = this;
107927
107928           for (var it = snapPts.iterator(); it.hasNext();) {
107929             var snapPt = it.next();
107930             var hotPixel = new HotPixel(snapPt, this$1._scaleFactor, this$1._li);
107931             this$1._pointSnapper.snap(hotPixel);
107932           }
107933         };
107934         MCIndexSnapRounder.prototype.interfaces_ = function interfaces_ () {
107935           return [Noder]
107936         };
107937         MCIndexSnapRounder.prototype.getClass = function getClass () {
107938           return MCIndexSnapRounder
107939         };
107940
107941         var BufferOp = function BufferOp () {
107942           this._argGeom = null;
107943           this._distance = null;
107944           this._bufParams = new BufferParameters();
107945           this._resultGeometry = null;
107946           this._saveException = null;
107947           if (arguments.length === 1) {
107948             var g = arguments[0];
107949             this._argGeom = g;
107950           } else if (arguments.length === 2) {
107951             var g$1 = arguments[0];
107952             var bufParams = arguments[1];
107953             this._argGeom = g$1;
107954             this._bufParams = bufParams;
107955           }
107956         };
107957
107958         var staticAccessors$32 = { CAP_ROUND: { configurable: true },CAP_BUTT: { configurable: true },CAP_FLAT: { configurable: true },CAP_SQUARE: { configurable: true },MAX_PRECISION_DIGITS: { configurable: true } };
107959         BufferOp.prototype.bufferFixedPrecision = function bufferFixedPrecision (fixedPM) {
107960           var noder = new ScaledNoder(new MCIndexSnapRounder(new PrecisionModel(1.0)), fixedPM.getScale());
107961           var bufBuilder = new BufferBuilder(this._bufParams);
107962           bufBuilder.setWorkingPrecisionModel(fixedPM);
107963           bufBuilder.setNoder(noder);
107964           this._resultGeometry = bufBuilder.buffer(this._argGeom, this._distance);
107965         };
107966         BufferOp.prototype.bufferReducedPrecision = function bufferReducedPrecision () {
107967             var this$1 = this;
107968
107969           if (arguments.length === 0) {
107970             for (var precDigits = BufferOp.MAX_PRECISION_DIGITS; precDigits >= 0; precDigits--) {
107971               try {
107972                 this$1.bufferReducedPrecision(precDigits);
107973               } catch (ex) {
107974                 if (ex instanceof TopologyException) {
107975                   this$1._saveException = ex;
107976                 } else { throw ex }
107977               } finally {}
107978               if (this$1._resultGeometry !== null) { return null }
107979             }
107980             throw this._saveException
107981           } else if (arguments.length === 1) {
107982             var precisionDigits = arguments[0];
107983             var sizeBasedScaleFactor = BufferOp.precisionScaleFactor(this._argGeom, this._distance, precisionDigits);
107984             var fixedPM = new PrecisionModel(sizeBasedScaleFactor);
107985             this.bufferFixedPrecision(fixedPM);
107986           }
107987         };
107988         BufferOp.prototype.computeGeometry = function computeGeometry () {
107989           this.bufferOriginalPrecision();
107990           if (this._resultGeometry !== null) { return null }
107991           var argPM = this._argGeom.getFactory().getPrecisionModel();
107992           if (argPM.getType() === PrecisionModel.FIXED) { this.bufferFixedPrecision(argPM); } else { this.bufferReducedPrecision(); }
107993         };
107994         BufferOp.prototype.setQuadrantSegments = function setQuadrantSegments (quadrantSegments) {
107995           this._bufParams.setQuadrantSegments(quadrantSegments);
107996         };
107997         BufferOp.prototype.bufferOriginalPrecision = function bufferOriginalPrecision () {
107998           try {
107999             var bufBuilder = new BufferBuilder(this._bufParams);
108000             this._resultGeometry = bufBuilder.buffer(this._argGeom, this._distance);
108001           } catch (ex) {
108002             if (ex instanceof RuntimeException) {
108003               this._saveException = ex;
108004             } else { throw ex }
108005           } finally {}
108006         };
108007         BufferOp.prototype.getResultGeometry = function getResultGeometry (distance) {
108008           this._distance = distance;
108009           this.computeGeometry();
108010           return this._resultGeometry
108011         };
108012         BufferOp.prototype.setEndCapStyle = function setEndCapStyle (endCapStyle) {
108013           this._bufParams.setEndCapStyle(endCapStyle);
108014         };
108015         BufferOp.prototype.interfaces_ = function interfaces_ () {
108016           return []
108017         };
108018         BufferOp.prototype.getClass = function getClass () {
108019           return BufferOp
108020         };
108021         BufferOp.bufferOp = function bufferOp () {
108022           if (arguments.length === 2) {
108023             var g = arguments[0];
108024             var distance = arguments[1];
108025             var gBuf = new BufferOp(g);
108026             var geomBuf = gBuf.getResultGeometry(distance);
108027             return geomBuf
108028           } else if (arguments.length === 3) {
108029             if (Number.isInteger(arguments[2]) && (arguments[0] instanceof Geometry && typeof arguments[1] === 'number')) {
108030               var g$1 = arguments[0];
108031               var distance$1 = arguments[1];
108032               var quadrantSegments = arguments[2];
108033               var bufOp = new BufferOp(g$1);
108034               bufOp.setQuadrantSegments(quadrantSegments);
108035               var geomBuf$1 = bufOp.getResultGeometry(distance$1);
108036               return geomBuf$1
108037             } else if (arguments[2] instanceof BufferParameters && (arguments[0] instanceof Geometry && typeof arguments[1] === 'number')) {
108038               var g$2 = arguments[0];
108039               var distance$2 = arguments[1];
108040               var params = arguments[2];
108041               var bufOp$1 = new BufferOp(g$2, params);
108042               var geomBuf$2 = bufOp$1.getResultGeometry(distance$2);
108043               return geomBuf$2
108044             }
108045           } else if (arguments.length === 4) {
108046             var g$3 = arguments[0];
108047             var distance$3 = arguments[1];
108048             var quadrantSegments$1 = arguments[2];
108049             var endCapStyle = arguments[3];
108050             var bufOp$2 = new BufferOp(g$3);
108051             bufOp$2.setQuadrantSegments(quadrantSegments$1);
108052             bufOp$2.setEndCapStyle(endCapStyle);
108053             var geomBuf$3 = bufOp$2.getResultGeometry(distance$3);
108054             return geomBuf$3
108055           }
108056         };
108057         BufferOp.precisionScaleFactor = function precisionScaleFactor (g, distance, maxPrecisionDigits) {
108058           var env = g.getEnvelopeInternal();
108059           var envMax = MathUtil.max(Math.abs(env.getMaxX()), Math.abs(env.getMaxY()), Math.abs(env.getMinX()), Math.abs(env.getMinY()));
108060           var expandByDistance = distance > 0.0 ? distance : 0.0;
108061           var bufEnvMax = envMax + 2 * expandByDistance;
108062           var bufEnvPrecisionDigits = Math.trunc(Math.log(bufEnvMax) / Math.log(10) + 1.0);
108063           var minUnitLog10 = maxPrecisionDigits - bufEnvPrecisionDigits;
108064           var scaleFactor = Math.pow(10.0, minUnitLog10);
108065           return scaleFactor
108066         };
108067         staticAccessors$32.CAP_ROUND.get = function () { return BufferParameters.CAP_ROUND };
108068         staticAccessors$32.CAP_BUTT.get = function () { return BufferParameters.CAP_FLAT };
108069         staticAccessors$32.CAP_FLAT.get = function () { return BufferParameters.CAP_FLAT };
108070         staticAccessors$32.CAP_SQUARE.get = function () { return BufferParameters.CAP_SQUARE };
108071         staticAccessors$32.MAX_PRECISION_DIGITS.get = function () { return 12 };
108072
108073         Object.defineProperties( BufferOp, staticAccessors$32 );
108074
108075         var PointPairDistance = function PointPairDistance () {
108076           this._pt = [new Coordinate(), new Coordinate()];
108077           this._distance = Double.NaN;
108078           this._isNull = true;
108079         };
108080         PointPairDistance.prototype.getCoordinates = function getCoordinates () {
108081           return this._pt
108082         };
108083         PointPairDistance.prototype.getCoordinate = function getCoordinate (i) {
108084           return this._pt[i]
108085         };
108086         PointPairDistance.prototype.setMinimum = function setMinimum () {
108087           if (arguments.length === 1) {
108088             var ptDist = arguments[0];
108089             this.setMinimum(ptDist._pt[0], ptDist._pt[1]);
108090           } else if (arguments.length === 2) {
108091             var p0 = arguments[0];
108092             var p1 = arguments[1];
108093             if (this._isNull) {
108094               this.initialize(p0, p1);
108095               return null
108096             }
108097             var dist = p0.distance(p1);
108098             if (dist < this._distance) { this.initialize(p0, p1, dist); }
108099           }
108100         };
108101         PointPairDistance.prototype.initialize = function initialize () {
108102           if (arguments.length === 0) {
108103             this._isNull = true;
108104           } else if (arguments.length === 2) {
108105             var p0 = arguments[0];
108106             var p1 = arguments[1];
108107             this._pt[0].setCoordinate(p0);
108108             this._pt[1].setCoordinate(p1);
108109             this._distance = p0.distance(p1);
108110             this._isNull = false;
108111           } else if (arguments.length === 3) {
108112             var p0$1 = arguments[0];
108113             var p1$1 = arguments[1];
108114             var distance = arguments[2];
108115             this._pt[0].setCoordinate(p0$1);
108116             this._pt[1].setCoordinate(p1$1);
108117             this._distance = distance;
108118             this._isNull = false;
108119           }
108120         };
108121         PointPairDistance.prototype.getDistance = function getDistance () {
108122           return this._distance
108123         };
108124         PointPairDistance.prototype.setMaximum = function setMaximum () {
108125           if (arguments.length === 1) {
108126             var ptDist = arguments[0];
108127             this.setMaximum(ptDist._pt[0], ptDist._pt[1]);
108128           } else if (arguments.length === 2) {
108129             var p0 = arguments[0];
108130             var p1 = arguments[1];
108131             if (this._isNull) {
108132               this.initialize(p0, p1);
108133               return null
108134             }
108135             var dist = p0.distance(p1);
108136             if (dist > this._distance) { this.initialize(p0, p1, dist); }
108137           }
108138         };
108139         PointPairDistance.prototype.interfaces_ = function interfaces_ () {
108140           return []
108141         };
108142         PointPairDistance.prototype.getClass = function getClass () {
108143           return PointPairDistance
108144         };
108145
108146         var DistanceToPointFinder = function DistanceToPointFinder () {};
108147
108148         DistanceToPointFinder.prototype.interfaces_ = function interfaces_ () {
108149           return []
108150         };
108151         DistanceToPointFinder.prototype.getClass = function getClass () {
108152           return DistanceToPointFinder
108153         };
108154         DistanceToPointFinder.computeDistance = function computeDistance () {
108155           if (arguments[2] instanceof PointPairDistance && (arguments[0] instanceof LineString && arguments[1] instanceof Coordinate)) {
108156             var line = arguments[0];
108157             var pt = arguments[1];
108158             var ptDist = arguments[2];
108159             var coords = line.getCoordinates();
108160             var tempSegment = new LineSegment();
108161             for (var i = 0; i < coords.length - 1; i++) {
108162               tempSegment.setCoordinates(coords[i], coords[i + 1]);
108163               var closestPt = tempSegment.closestPoint(pt);
108164               ptDist.setMinimum(closestPt, pt);
108165             }
108166           } else if (arguments[2] instanceof PointPairDistance && (arguments[0] instanceof Polygon && arguments[1] instanceof Coordinate)) {
108167             var poly = arguments[0];
108168             var pt$1 = arguments[1];
108169             var ptDist$1 = arguments[2];
108170             DistanceToPointFinder.computeDistance(poly.getExteriorRing(), pt$1, ptDist$1);
108171             for (var i$1 = 0; i$1 < poly.getNumInteriorRing(); i$1++) {
108172               DistanceToPointFinder.computeDistance(poly.getInteriorRingN(i$1), pt$1, ptDist$1);
108173             }
108174           } else if (arguments[2] instanceof PointPairDistance && (arguments[0] instanceof Geometry && arguments[1] instanceof Coordinate)) {
108175             var geom = arguments[0];
108176             var pt$2 = arguments[1];
108177             var ptDist$2 = arguments[2];
108178             if (geom instanceof LineString) {
108179               DistanceToPointFinder.computeDistance(geom, pt$2, ptDist$2);
108180             } else if (geom instanceof Polygon) {
108181               DistanceToPointFinder.computeDistance(geom, pt$2, ptDist$2);
108182             } else if (geom instanceof GeometryCollection) {
108183               var gc = geom;
108184               for (var i$2 = 0; i$2 < gc.getNumGeometries(); i$2++) {
108185                 var g = gc.getGeometryN(i$2);
108186                 DistanceToPointFinder.computeDistance(g, pt$2, ptDist$2);
108187               }
108188             } else {
108189               ptDist$2.setMinimum(geom.getCoordinate(), pt$2);
108190             }
108191           } else if (arguments[2] instanceof PointPairDistance && (arguments[0] instanceof LineSegment && arguments[1] instanceof Coordinate)) {
108192             var segment = arguments[0];
108193             var pt$3 = arguments[1];
108194             var ptDist$3 = arguments[2];
108195             var closestPt$1 = segment.closestPoint(pt$3);
108196             ptDist$3.setMinimum(closestPt$1, pt$3);
108197           }
108198         };
108199
108200         var BufferCurveMaximumDistanceFinder = function BufferCurveMaximumDistanceFinder (inputGeom) {
108201           this._maxPtDist = new PointPairDistance();
108202           this._inputGeom = inputGeom || null;
108203         };
108204
108205         var staticAccessors$36 = { MaxPointDistanceFilter: { configurable: true },MaxMidpointDistanceFilter: { configurable: true } };
108206         BufferCurveMaximumDistanceFinder.prototype.computeMaxMidpointDistance = function computeMaxMidpointDistance (curve) {
108207           var distFilter = new MaxMidpointDistanceFilter(this._inputGeom);
108208           curve.apply(distFilter);
108209           this._maxPtDist.setMaximum(distFilter.getMaxPointDistance());
108210         };
108211         BufferCurveMaximumDistanceFinder.prototype.computeMaxVertexDistance = function computeMaxVertexDistance (curve) {
108212           var distFilter = new MaxPointDistanceFilter(this._inputGeom);
108213           curve.apply(distFilter);
108214           this._maxPtDist.setMaximum(distFilter.getMaxPointDistance());
108215         };
108216         BufferCurveMaximumDistanceFinder.prototype.findDistance = function findDistance (bufferCurve) {
108217           this.computeMaxVertexDistance(bufferCurve);
108218           this.computeMaxMidpointDistance(bufferCurve);
108219           return this._maxPtDist.getDistance()
108220         };
108221         BufferCurveMaximumDistanceFinder.prototype.getDistancePoints = function getDistancePoints () {
108222           return this._maxPtDist
108223         };
108224         BufferCurveMaximumDistanceFinder.prototype.interfaces_ = function interfaces_ () {
108225           return []
108226         };
108227         BufferCurveMaximumDistanceFinder.prototype.getClass = function getClass () {
108228           return BufferCurveMaximumDistanceFinder
108229         };
108230         staticAccessors$36.MaxPointDistanceFilter.get = function () { return MaxPointDistanceFilter };
108231         staticAccessors$36.MaxMidpointDistanceFilter.get = function () { return MaxMidpointDistanceFilter };
108232
108233         Object.defineProperties( BufferCurveMaximumDistanceFinder, staticAccessors$36 );
108234
108235         var MaxPointDistanceFilter = function MaxPointDistanceFilter (geom) {
108236           this._maxPtDist = new PointPairDistance();
108237           this._minPtDist = new PointPairDistance();
108238           this._geom = geom || null;
108239         };
108240         MaxPointDistanceFilter.prototype.filter = function filter (pt) {
108241           this._minPtDist.initialize();
108242           DistanceToPointFinder.computeDistance(this._geom, pt, this._minPtDist);
108243           this._maxPtDist.setMaximum(this._minPtDist);
108244         };
108245         MaxPointDistanceFilter.prototype.getMaxPointDistance = function getMaxPointDistance () {
108246           return this._maxPtDist
108247         };
108248         MaxPointDistanceFilter.prototype.interfaces_ = function interfaces_ () {
108249           return [CoordinateFilter]
108250         };
108251         MaxPointDistanceFilter.prototype.getClass = function getClass () {
108252           return MaxPointDistanceFilter
108253         };
108254
108255         var MaxMidpointDistanceFilter = function MaxMidpointDistanceFilter (geom) {
108256           this._maxPtDist = new PointPairDistance();
108257           this._minPtDist = new PointPairDistance();
108258           this._geom = geom || null;
108259         };
108260         MaxMidpointDistanceFilter.prototype.filter = function filter (seq, index) {
108261           if (index === 0) { return null }
108262           var p0 = seq.getCoordinate(index - 1);
108263           var p1 = seq.getCoordinate(index);
108264           var midPt = new Coordinate((p0.x + p1.x) / 2, (p0.y + p1.y) / 2);
108265           this._minPtDist.initialize();
108266           DistanceToPointFinder.computeDistance(this._geom, midPt, this._minPtDist);
108267           this._maxPtDist.setMaximum(this._minPtDist);
108268         };
108269         MaxMidpointDistanceFilter.prototype.isDone = function isDone () {
108270           return false
108271         };
108272         MaxMidpointDistanceFilter.prototype.isGeometryChanged = function isGeometryChanged () {
108273           return false
108274         };
108275         MaxMidpointDistanceFilter.prototype.getMaxPointDistance = function getMaxPointDistance () {
108276           return this._maxPtDist
108277         };
108278         MaxMidpointDistanceFilter.prototype.interfaces_ = function interfaces_ () {
108279           return [CoordinateSequenceFilter]
108280         };
108281         MaxMidpointDistanceFilter.prototype.getClass = function getClass () {
108282           return MaxMidpointDistanceFilter
108283         };
108284
108285         var PolygonExtracter = function PolygonExtracter (comps) {
108286           this._comps = comps || null;
108287         };
108288         PolygonExtracter.prototype.filter = function filter (geom) {
108289           if (geom instanceof Polygon) { this._comps.add(geom); }
108290         };
108291         PolygonExtracter.prototype.interfaces_ = function interfaces_ () {
108292           return [GeometryFilter]
108293         };
108294         PolygonExtracter.prototype.getClass = function getClass () {
108295           return PolygonExtracter
108296         };
108297         PolygonExtracter.getPolygons = function getPolygons () {
108298           if (arguments.length === 1) {
108299             var geom = arguments[0];
108300             return PolygonExtracter.getPolygons(geom, new ArrayList())
108301           } else if (arguments.length === 2) {
108302             var geom$1 = arguments[0];
108303             var list = arguments[1];
108304             if (geom$1 instanceof Polygon) {
108305               list.add(geom$1);
108306             } else if (geom$1 instanceof GeometryCollection) {
108307               geom$1.apply(new PolygonExtracter(list));
108308             }
108309             return list
108310           }
108311         };
108312
108313         var LinearComponentExtracter = function LinearComponentExtracter () {
108314           this._lines = null;
108315           this._isForcedToLineString = false;
108316           if (arguments.length === 1) {
108317             var lines = arguments[0];
108318             this._lines = lines;
108319           } else if (arguments.length === 2) {
108320             var lines$1 = arguments[0];
108321             var isForcedToLineString = arguments[1];
108322             this._lines = lines$1;
108323             this._isForcedToLineString = isForcedToLineString;
108324           }
108325         };
108326         LinearComponentExtracter.prototype.filter = function filter (geom) {
108327           if (this._isForcedToLineString && geom instanceof LinearRing) {
108328             var line = geom.getFactory().createLineString(geom.getCoordinateSequence());
108329             this._lines.add(line);
108330             return null
108331           }
108332           if (geom instanceof LineString) { this._lines.add(geom); }
108333         };
108334         LinearComponentExtracter.prototype.setForceToLineString = function setForceToLineString (isForcedToLineString) {
108335           this._isForcedToLineString = isForcedToLineString;
108336         };
108337         LinearComponentExtracter.prototype.interfaces_ = function interfaces_ () {
108338           return [GeometryComponentFilter]
108339         };
108340         LinearComponentExtracter.prototype.getClass = function getClass () {
108341           return LinearComponentExtracter
108342         };
108343         LinearComponentExtracter.getGeometry = function getGeometry () {
108344           if (arguments.length === 1) {
108345             var geom = arguments[0];
108346             return geom.getFactory().buildGeometry(LinearComponentExtracter.getLines(geom))
108347           } else if (arguments.length === 2) {
108348             var geom$1 = arguments[0];
108349             var forceToLineString = arguments[1];
108350             return geom$1.getFactory().buildGeometry(LinearComponentExtracter.getLines(geom$1, forceToLineString))
108351           }
108352         };
108353         LinearComponentExtracter.getLines = function getLines () {
108354           if (arguments.length === 1) {
108355             var geom = arguments[0];
108356             return LinearComponentExtracter.getLines(geom, false)
108357           } else if (arguments.length === 2) {
108358             if (hasInterface(arguments[0], Collection) && hasInterface(arguments[1], Collection)) {
108359               var geoms = arguments[0];
108360               var lines$1 = arguments[1];
108361               for (var i = geoms.iterator(); i.hasNext();) {
108362                 var g = i.next();
108363                 LinearComponentExtracter.getLines(g, lines$1);
108364               }
108365               return lines$1
108366             } else if (arguments[0] instanceof Geometry && typeof arguments[1] === 'boolean') {
108367               var geom$1 = arguments[0];
108368               var forceToLineString = arguments[1];
108369               var lines = new ArrayList();
108370               geom$1.apply(new LinearComponentExtracter(lines, forceToLineString));
108371               return lines
108372             } else if (arguments[0] instanceof Geometry && hasInterface(arguments[1], Collection)) {
108373               var geom$2 = arguments[0];
108374               var lines$2 = arguments[1];
108375               if (geom$2 instanceof LineString) {
108376                 lines$2.add(geom$2);
108377               } else {
108378                 geom$2.apply(new LinearComponentExtracter(lines$2));
108379               }
108380               return lines$2
108381             }
108382           } else if (arguments.length === 3) {
108383             if (typeof arguments[2] === 'boolean' && (hasInterface(arguments[0], Collection) && hasInterface(arguments[1], Collection))) {
108384               var geoms$1 = arguments[0];
108385               var lines$3 = arguments[1];
108386               var forceToLineString$1 = arguments[2];
108387               for (var i$1 = geoms$1.iterator(); i$1.hasNext();) {
108388                 var g$1 = i$1.next();
108389                 LinearComponentExtracter.getLines(g$1, lines$3, forceToLineString$1);
108390               }
108391               return lines$3
108392             } else if (typeof arguments[2] === 'boolean' && (arguments[0] instanceof Geometry && hasInterface(arguments[1], Collection))) {
108393               var geom$3 = arguments[0];
108394               var lines$4 = arguments[1];
108395               var forceToLineString$2 = arguments[2];
108396               geom$3.apply(new LinearComponentExtracter(lines$4, forceToLineString$2));
108397               return lines$4
108398             }
108399           }
108400         };
108401
108402         var PointLocator = function PointLocator () {
108403           this._boundaryRule = BoundaryNodeRule.OGC_SFS_BOUNDARY_RULE;
108404           this._isIn = null;
108405           this._numBoundaries = null;
108406           if (arguments.length === 0) ; else if (arguments.length === 1) {
108407             var boundaryRule = arguments[0];
108408             if (boundaryRule === null) { throw new IllegalArgumentException('Rule must be non-null') }
108409             this._boundaryRule = boundaryRule;
108410           }
108411         };
108412         PointLocator.prototype.locateInternal = function locateInternal () {
108413             var this$1 = this;
108414
108415           if (arguments[0] instanceof Coordinate && arguments[1] instanceof Polygon) {
108416             var p = arguments[0];
108417             var poly = arguments[1];
108418             if (poly.isEmpty()) { return Location.EXTERIOR }
108419             var shell = poly.getExteriorRing();
108420             var shellLoc = this.locateInPolygonRing(p, shell);
108421             if (shellLoc === Location.EXTERIOR) { return Location.EXTERIOR }
108422             if (shellLoc === Location.BOUNDARY) { return Location.BOUNDARY }
108423             for (var i = 0; i < poly.getNumInteriorRing(); i++) {
108424               var hole = poly.getInteriorRingN(i);
108425               var holeLoc = this$1.locateInPolygonRing(p, hole);
108426               if (holeLoc === Location.INTERIOR) { return Location.EXTERIOR }
108427               if (holeLoc === Location.BOUNDARY) { return Location.BOUNDARY }
108428             }
108429             return Location.INTERIOR
108430           } else if (arguments[0] instanceof Coordinate && arguments[1] instanceof LineString) {
108431             var p$1 = arguments[0];
108432             var l = arguments[1];
108433             if (!l.getEnvelopeInternal().intersects(p$1)) { return Location.EXTERIOR }
108434             var pt = l.getCoordinates();
108435             if (!l.isClosed()) {
108436               if (p$1.equals(pt[0]) || p$1.equals(pt[pt.length - 1])) {
108437                 return Location.BOUNDARY
108438               }
108439             }
108440             if (CGAlgorithms.isOnLine(p$1, pt)) { return Location.INTERIOR }
108441             return Location.EXTERIOR
108442           } else if (arguments[0] instanceof Coordinate && arguments[1] instanceof Point$1) {
108443             var p$2 = arguments[0];
108444             var pt$1 = arguments[1];
108445             var ptCoord = pt$1.getCoordinate();
108446             if (ptCoord.equals2D(p$2)) { return Location.INTERIOR }
108447             return Location.EXTERIOR
108448           }
108449         };
108450         PointLocator.prototype.locateInPolygonRing = function locateInPolygonRing (p, ring) {
108451           if (!ring.getEnvelopeInternal().intersects(p)) { return Location.EXTERIOR }
108452           return CGAlgorithms.locatePointInRing(p, ring.getCoordinates())
108453         };
108454         PointLocator.prototype.intersects = function intersects (p, geom) {
108455           return this.locate(p, geom) !== Location.EXTERIOR
108456         };
108457         PointLocator.prototype.updateLocationInfo = function updateLocationInfo (loc) {
108458           if (loc === Location.INTERIOR) { this._isIn = true; }
108459           if (loc === Location.BOUNDARY) { this._numBoundaries++; }
108460         };
108461         PointLocator.prototype.computeLocation = function computeLocation (p, geom) {
108462             var this$1 = this;
108463
108464           if (geom instanceof Point$1) {
108465             this.updateLocationInfo(this.locateInternal(p, geom));
108466           }
108467           if (geom instanceof LineString) {
108468             this.updateLocationInfo(this.locateInternal(p, geom));
108469           } else if (geom instanceof Polygon) {
108470             this.updateLocationInfo(this.locateInternal(p, geom));
108471           } else if (geom instanceof MultiLineString) {
108472             var ml = geom;
108473             for (var i = 0; i < ml.getNumGeometries(); i++) {
108474               var l = ml.getGeometryN(i);
108475               this$1.updateLocationInfo(this$1.locateInternal(p, l));
108476             }
108477           } else if (geom instanceof MultiPolygon) {
108478             var mpoly = geom;
108479             for (var i$1 = 0; i$1 < mpoly.getNumGeometries(); i$1++) {
108480               var poly = mpoly.getGeometryN(i$1);
108481               this$1.updateLocationInfo(this$1.locateInternal(p, poly));
108482             }
108483           } else if (geom instanceof GeometryCollection) {
108484             var geomi = new GeometryCollectionIterator(geom);
108485             while (geomi.hasNext()) {
108486               var g2 = geomi.next();
108487               if (g2 !== geom) { this$1.computeLocation(p, g2); }
108488             }
108489           }
108490         };
108491         PointLocator.prototype.locate = function locate (p, geom) {
108492           if (geom.isEmpty()) { return Location.EXTERIOR }
108493           if (geom instanceof LineString) {
108494             return this.locateInternal(p, geom)
108495           } else if (geom instanceof Polygon) {
108496             return this.locateInternal(p, geom)
108497           }
108498           this._isIn = false;
108499           this._numBoundaries = 0;
108500           this.computeLocation(p, geom);
108501           if (this._boundaryRule.isInBoundary(this._numBoundaries)) { return Location.BOUNDARY }
108502           if (this._numBoundaries > 0 || this._isIn) { return Location.INTERIOR }
108503           return Location.EXTERIOR
108504         };
108505         PointLocator.prototype.interfaces_ = function interfaces_ () {
108506           return []
108507         };
108508         PointLocator.prototype.getClass = function getClass () {
108509           return PointLocator
108510         };
108511
108512         var GeometryLocation = function GeometryLocation () {
108513           this._component = null;
108514           this._segIndex = null;
108515           this._pt = null;
108516           if (arguments.length === 2) {
108517             var component = arguments[0];
108518             var pt = arguments[1];
108519             GeometryLocation.call(this, component, GeometryLocation.INSIDE_AREA, pt);
108520           } else if (arguments.length === 3) {
108521             var component$1 = arguments[0];
108522             var segIndex = arguments[1];
108523             var pt$1 = arguments[2];
108524             this._component = component$1;
108525             this._segIndex = segIndex;
108526             this._pt = pt$1;
108527           }
108528         };
108529
108530         var staticAccessors$38 = { INSIDE_AREA: { configurable: true } };
108531         GeometryLocation.prototype.isInsideArea = function isInsideArea () {
108532           return this._segIndex === GeometryLocation.INSIDE_AREA
108533         };
108534         GeometryLocation.prototype.getCoordinate = function getCoordinate () {
108535           return this._pt
108536         };
108537         GeometryLocation.prototype.getGeometryComponent = function getGeometryComponent () {
108538           return this._component
108539         };
108540         GeometryLocation.prototype.getSegmentIndex = function getSegmentIndex () {
108541           return this._segIndex
108542         };
108543         GeometryLocation.prototype.interfaces_ = function interfaces_ () {
108544           return []
108545         };
108546         GeometryLocation.prototype.getClass = function getClass () {
108547           return GeometryLocation
108548         };
108549         staticAccessors$38.INSIDE_AREA.get = function () { return -1 };
108550
108551         Object.defineProperties( GeometryLocation, staticAccessors$38 );
108552
108553         var PointExtracter = function PointExtracter (pts) {
108554           this._pts = pts || null;
108555         };
108556         PointExtracter.prototype.filter = function filter (geom) {
108557           if (geom instanceof Point$1) { this._pts.add(geom); }
108558         };
108559         PointExtracter.prototype.interfaces_ = function interfaces_ () {
108560           return [GeometryFilter]
108561         };
108562         PointExtracter.prototype.getClass = function getClass () {
108563           return PointExtracter
108564         };
108565         PointExtracter.getPoints = function getPoints () {
108566           if (arguments.length === 1) {
108567             var geom = arguments[0];
108568             if (geom instanceof Point$1) {
108569               return Collections.singletonList(geom)
108570             }
108571             return PointExtracter.getPoints(geom, new ArrayList())
108572           } else if (arguments.length === 2) {
108573             var geom$1 = arguments[0];
108574             var list = arguments[1];
108575             if (geom$1 instanceof Point$1) {
108576               list.add(geom$1);
108577             } else if (geom$1 instanceof GeometryCollection) {
108578               geom$1.apply(new PointExtracter(list));
108579             }
108580             return list
108581           }
108582         };
108583
108584         var ConnectedElementLocationFilter = function ConnectedElementLocationFilter () {
108585           this._locations = null;
108586           var locations = arguments[0];
108587           this._locations = locations;
108588         };
108589         ConnectedElementLocationFilter.prototype.filter = function filter (geom) {
108590           if (geom instanceof Point$1 || geom instanceof LineString || geom instanceof Polygon) { this._locations.add(new GeometryLocation(geom, 0, geom.getCoordinate())); }
108591         };
108592         ConnectedElementLocationFilter.prototype.interfaces_ = function interfaces_ () {
108593           return [GeometryFilter]
108594         };
108595         ConnectedElementLocationFilter.prototype.getClass = function getClass () {
108596           return ConnectedElementLocationFilter
108597         };
108598         ConnectedElementLocationFilter.getLocations = function getLocations (geom) {
108599           var locations = new ArrayList();
108600           geom.apply(new ConnectedElementLocationFilter(locations));
108601           return locations
108602         };
108603
108604         var DistanceOp = function DistanceOp () {
108605           this._geom = null;
108606           this._terminateDistance = 0.0;
108607           this._ptLocator = new PointLocator();
108608           this._minDistanceLocation = null;
108609           this._minDistance = Double.MAX_VALUE;
108610           if (arguments.length === 2) {
108611             var g0 = arguments[0];
108612             var g1 = arguments[1];
108613             this._geom = [g0, g1];
108614             this._terminateDistance = 0.0;
108615           } else if (arguments.length === 3) {
108616             var g0$1 = arguments[0];
108617             var g1$1 = arguments[1];
108618             var terminateDistance = arguments[2];
108619             this._geom = new Array(2).fill(null);
108620             this._geom[0] = g0$1;
108621             this._geom[1] = g1$1;
108622             this._terminateDistance = terminateDistance;
108623           }
108624         };
108625         DistanceOp.prototype.computeContainmentDistance = function computeContainmentDistance () {
108626             var this$1 = this;
108627
108628           if (arguments.length === 0) {
108629             var locPtPoly = new Array(2).fill(null);
108630             this.computeContainmentDistance(0, locPtPoly);
108631             if (this._minDistance <= this._terminateDistance) { return null }
108632             this.computeContainmentDistance(1, locPtPoly);
108633           } else if (arguments.length === 2) {
108634             var polyGeomIndex = arguments[0];
108635             var locPtPoly$1 = arguments[1];
108636             var locationsIndex = 1 - polyGeomIndex;
108637             var polys = PolygonExtracter.getPolygons(this._geom[polyGeomIndex]);
108638             if (polys.size() > 0) {
108639               var insideLocs = ConnectedElementLocationFilter.getLocations(this._geom[locationsIndex]);
108640               this.computeContainmentDistance(insideLocs, polys, locPtPoly$1);
108641               if (this._minDistance <= this._terminateDistance) {
108642                 this._minDistanceLocation[locationsIndex] = locPtPoly$1[0];
108643                 this._minDistanceLocation[polyGeomIndex] = locPtPoly$1[1];
108644                 return null
108645               }
108646             }
108647           } else if (arguments.length === 3) {
108648             if (arguments[2] instanceof Array && (hasInterface(arguments[0], List) && hasInterface(arguments[1], List))) {
108649               var locs = arguments[0];
108650               var polys$1 = arguments[1];
108651               var locPtPoly$2 = arguments[2];
108652               for (var i = 0; i < locs.size(); i++) {
108653                 var loc = locs.get(i);
108654                 for (var j = 0; j < polys$1.size(); j++) {
108655                   this$1.computeContainmentDistance(loc, polys$1.get(j), locPtPoly$2);
108656                   if (this$1._minDistance <= this$1._terminateDistance) { return null }
108657                 }
108658               }
108659             } else if (arguments[2] instanceof Array && (arguments[0] instanceof GeometryLocation && arguments[1] instanceof Polygon)) {
108660               var ptLoc = arguments[0];
108661               var poly = arguments[1];
108662               var locPtPoly$3 = arguments[2];
108663               var pt = ptLoc.getCoordinate();
108664               if (Location.EXTERIOR !== this._ptLocator.locate(pt, poly)) {
108665                 this._minDistance = 0.0;
108666                 locPtPoly$3[0] = ptLoc;
108667                 locPtPoly$3[1] = new GeometryLocation(poly, pt);
108668
108669                 return null
108670               }
108671             }
108672           }
108673         };
108674         DistanceOp.prototype.computeMinDistanceLinesPoints = function computeMinDistanceLinesPoints (lines, points, locGeom) {
108675             var this$1 = this;
108676
108677           for (var i = 0; i < lines.size(); i++) {
108678             var line = lines.get(i);
108679             for (var j = 0; j < points.size(); j++) {
108680               var pt = points.get(j);
108681               this$1.computeMinDistance(line, pt, locGeom);
108682               if (this$1._minDistance <= this$1._terminateDistance) { return null }
108683             }
108684           }
108685         };
108686         DistanceOp.prototype.computeFacetDistance = function computeFacetDistance () {
108687           var locGeom = new Array(2).fill(null);
108688           var lines0 = LinearComponentExtracter.getLines(this._geom[0]);
108689           var lines1 = LinearComponentExtracter.getLines(this._geom[1]);
108690           var pts0 = PointExtracter.getPoints(this._geom[0]);
108691           var pts1 = PointExtracter.getPoints(this._geom[1]);
108692           this.computeMinDistanceLines(lines0, lines1, locGeom);
108693           this.updateMinDistance(locGeom, false);
108694           if (this._minDistance <= this._terminateDistance) { return null }
108695           locGeom[0] = null;
108696           locGeom[1] = null;
108697           this.computeMinDistanceLinesPoints(lines0, pts1, locGeom);
108698           this.updateMinDistance(locGeom, false);
108699           if (this._minDistance <= this._terminateDistance) { return null }
108700           locGeom[0] = null;
108701           locGeom[1] = null;
108702           this.computeMinDistanceLinesPoints(lines1, pts0, locGeom);
108703           this.updateMinDistance(locGeom, true);
108704           if (this._minDistance <= this._terminateDistance) { return null }
108705           locGeom[0] = null;
108706           locGeom[1] = null;
108707           this.computeMinDistancePoints(pts0, pts1, locGeom);
108708           this.updateMinDistance(locGeom, false);
108709         };
108710         DistanceOp.prototype.nearestLocations = function nearestLocations () {
108711           this.computeMinDistance();
108712           return this._minDistanceLocation
108713         };
108714         DistanceOp.prototype.updateMinDistance = function updateMinDistance (locGeom, flip) {
108715           if (locGeom[0] === null) { return null }
108716           if (flip) {
108717             this._minDistanceLocation[0] = locGeom[1];
108718             this._minDistanceLocation[1] = locGeom[0];
108719           } else {
108720             this._minDistanceLocation[0] = locGeom[0];
108721             this._minDistanceLocation[1] = locGeom[1];
108722           }
108723         };
108724         DistanceOp.prototype.nearestPoints = function nearestPoints () {
108725           this.computeMinDistance();
108726           var nearestPts = [this._minDistanceLocation[0].getCoordinate(), this._minDistanceLocation[1].getCoordinate()];
108727           return nearestPts
108728         };
108729         DistanceOp.prototype.computeMinDistance = function computeMinDistance () {
108730             var this$1 = this;
108731
108732           if (arguments.length === 0) {
108733             if (this._minDistanceLocation !== null) { return null }
108734             this._minDistanceLocation = new Array(2).fill(null);
108735             this.computeContainmentDistance();
108736             if (this._minDistance <= this._terminateDistance) { return null }
108737             this.computeFacetDistance();
108738           } else if (arguments.length === 3) {
108739             if (arguments[2] instanceof Array && (arguments[0] instanceof LineString && arguments[1] instanceof Point$1)) {
108740               var line = arguments[0];
108741               var pt = arguments[1];
108742               var locGeom = arguments[2];
108743               if (line.getEnvelopeInternal().distance(pt.getEnvelopeInternal()) > this._minDistance) { return null }
108744               var coord0 = line.getCoordinates();
108745               var coord = pt.getCoordinate();
108746               for (var i = 0; i < coord0.length - 1; i++) {
108747                 var dist = CGAlgorithms.distancePointLine(coord, coord0[i], coord0[i + 1]);
108748                 if (dist < this$1._minDistance) {
108749                   this$1._minDistance = dist;
108750                   var seg = new LineSegment(coord0[i], coord0[i + 1]);
108751                   var segClosestPoint = seg.closestPoint(coord);
108752                   locGeom[0] = new GeometryLocation(line, i, segClosestPoint);
108753                   locGeom[1] = new GeometryLocation(pt, 0, coord);
108754                 }
108755                 if (this$1._minDistance <= this$1._terminateDistance) { return null }
108756               }
108757             } else if (arguments[2] instanceof Array && (arguments[0] instanceof LineString && arguments[1] instanceof LineString)) {
108758               var line0 = arguments[0];
108759               var line1 = arguments[1];
108760               var locGeom$1 = arguments[2];
108761               if (line0.getEnvelopeInternal().distance(line1.getEnvelopeInternal()) > this._minDistance) { return null }
108762               var coord0$1 = line0.getCoordinates();
108763               var coord1 = line1.getCoordinates();
108764               for (var i$1 = 0; i$1 < coord0$1.length - 1; i$1++) {
108765                 for (var j = 0; j < coord1.length - 1; j++) {
108766                   var dist$1 = CGAlgorithms.distanceLineLine(coord0$1[i$1], coord0$1[i$1 + 1], coord1[j], coord1[j + 1]);
108767                   if (dist$1 < this$1._minDistance) {
108768                     this$1._minDistance = dist$1;
108769                     var seg0 = new LineSegment(coord0$1[i$1], coord0$1[i$1 + 1]);
108770                     var seg1 = new LineSegment(coord1[j], coord1[j + 1]);
108771                     var closestPt = seg0.closestPoints(seg1);
108772                     locGeom$1[0] = new GeometryLocation(line0, i$1, closestPt[0]);
108773                     locGeom$1[1] = new GeometryLocation(line1, j, closestPt[1]);
108774                   }
108775                   if (this$1._minDistance <= this$1._terminateDistance) { return null }
108776                 }
108777               }
108778             }
108779           }
108780         };
108781         DistanceOp.prototype.computeMinDistancePoints = function computeMinDistancePoints (points0, points1, locGeom) {
108782             var this$1 = this;
108783
108784           for (var i = 0; i < points0.size(); i++) {
108785             var pt0 = points0.get(i);
108786             for (var j = 0; j < points1.size(); j++) {
108787               var pt1 = points1.get(j);
108788               var dist = pt0.getCoordinate().distance(pt1.getCoordinate());
108789               if (dist < this$1._minDistance) {
108790                 this$1._minDistance = dist;
108791                 locGeom[0] = new GeometryLocation(pt0, 0, pt0.getCoordinate());
108792                 locGeom[1] = new GeometryLocation(pt1, 0, pt1.getCoordinate());
108793               }
108794               if (this$1._minDistance <= this$1._terminateDistance) { return null }
108795             }
108796           }
108797         };
108798         DistanceOp.prototype.distance = function distance () {
108799           if (this._geom[0] === null || this._geom[1] === null) { throw new IllegalArgumentException('null geometries are not supported') }
108800           if (this._geom[0].isEmpty() || this._geom[1].isEmpty()) { return 0.0 }
108801           this.computeMinDistance();
108802           return this._minDistance
108803         };
108804         DistanceOp.prototype.computeMinDistanceLines = function computeMinDistanceLines (lines0, lines1, locGeom) {
108805             var this$1 = this;
108806
108807           for (var i = 0; i < lines0.size(); i++) {
108808             var line0 = lines0.get(i);
108809             for (var j = 0; j < lines1.size(); j++) {
108810               var line1 = lines1.get(j);
108811               this$1.computeMinDistance(line0, line1, locGeom);
108812               if (this$1._minDistance <= this$1._terminateDistance) { return null }
108813             }
108814           }
108815         };
108816         DistanceOp.prototype.interfaces_ = function interfaces_ () {
108817           return []
108818         };
108819         DistanceOp.prototype.getClass = function getClass () {
108820           return DistanceOp
108821         };
108822         DistanceOp.distance = function distance (g0, g1) {
108823           var distOp = new DistanceOp(g0, g1);
108824           return distOp.distance()
108825         };
108826         DistanceOp.isWithinDistance = function isWithinDistance (g0, g1, distance) {
108827           var distOp = new DistanceOp(g0, g1, distance);
108828           return distOp.distance() <= distance
108829         };
108830         DistanceOp.nearestPoints = function nearestPoints (g0, g1) {
108831           var distOp = new DistanceOp(g0, g1);
108832           return distOp.nearestPoints()
108833         };
108834
108835         var PointPairDistance$2 = function PointPairDistance () {
108836           this._pt = [new Coordinate(), new Coordinate()];
108837           this._distance = Double.NaN;
108838           this._isNull = true;
108839         };
108840         PointPairDistance$2.prototype.getCoordinates = function getCoordinates () {
108841           return this._pt
108842         };
108843         PointPairDistance$2.prototype.getCoordinate = function getCoordinate (i) {
108844           return this._pt[i]
108845         };
108846         PointPairDistance$2.prototype.setMinimum = function setMinimum () {
108847           if (arguments.length === 1) {
108848             var ptDist = arguments[0];
108849             this.setMinimum(ptDist._pt[0], ptDist._pt[1]);
108850           } else if (arguments.length === 2) {
108851             var p0 = arguments[0];
108852             var p1 = arguments[1];
108853             if (this._isNull) {
108854               this.initialize(p0, p1);
108855               return null
108856             }
108857             var dist = p0.distance(p1);
108858             if (dist < this._distance) { this.initialize(p0, p1, dist); }
108859           }
108860         };
108861         PointPairDistance$2.prototype.initialize = function initialize () {
108862           if (arguments.length === 0) {
108863             this._isNull = true;
108864           } else if (arguments.length === 2) {
108865             var p0 = arguments[0];
108866             var p1 = arguments[1];
108867             this._pt[0].setCoordinate(p0);
108868             this._pt[1].setCoordinate(p1);
108869             this._distance = p0.distance(p1);
108870             this._isNull = false;
108871           } else if (arguments.length === 3) {
108872             var p0$1 = arguments[0];
108873             var p1$1 = arguments[1];
108874             var distance = arguments[2];
108875             this._pt[0].setCoordinate(p0$1);
108876             this._pt[1].setCoordinate(p1$1);
108877             this._distance = distance;
108878             this._isNull = false;
108879           }
108880         };
108881         PointPairDistance$2.prototype.toString = function toString () {
108882           return WKTWriter.toLineString(this._pt[0], this._pt[1])
108883         };
108884         PointPairDistance$2.prototype.getDistance = function getDistance () {
108885           return this._distance
108886         };
108887         PointPairDistance$2.prototype.setMaximum = function setMaximum () {
108888           if (arguments.length === 1) {
108889             var ptDist = arguments[0];
108890             this.setMaximum(ptDist._pt[0], ptDist._pt[1]);
108891           } else if (arguments.length === 2) {
108892             var p0 = arguments[0];
108893             var p1 = arguments[1];
108894             if (this._isNull) {
108895               this.initialize(p0, p1);
108896               return null
108897             }
108898             var dist = p0.distance(p1);
108899             if (dist > this._distance) { this.initialize(p0, p1, dist); }
108900           }
108901         };
108902         PointPairDistance$2.prototype.interfaces_ = function interfaces_ () {
108903           return []
108904         };
108905         PointPairDistance$2.prototype.getClass = function getClass () {
108906           return PointPairDistance$2
108907         };
108908
108909         var DistanceToPoint = function DistanceToPoint () {};
108910
108911         DistanceToPoint.prototype.interfaces_ = function interfaces_ () {
108912           return []
108913         };
108914         DistanceToPoint.prototype.getClass = function getClass () {
108915           return DistanceToPoint
108916         };
108917         DistanceToPoint.computeDistance = function computeDistance () {
108918           if (arguments[2] instanceof PointPairDistance$2 && (arguments[0] instanceof LineString && arguments[1] instanceof Coordinate)) {
108919             var line = arguments[0];
108920             var pt = arguments[1];
108921             var ptDist = arguments[2];
108922             var tempSegment = new LineSegment();
108923             var coords = line.getCoordinates();
108924             for (var i = 0; i < coords.length - 1; i++) {
108925               tempSegment.setCoordinates(coords[i], coords[i + 1]);
108926               var closestPt = tempSegment.closestPoint(pt);
108927               ptDist.setMinimum(closestPt, pt);
108928             }
108929           } else if (arguments[2] instanceof PointPairDistance$2 && (arguments[0] instanceof Polygon && arguments[1] instanceof Coordinate)) {
108930             var poly = arguments[0];
108931             var pt$1 = arguments[1];
108932             var ptDist$1 = arguments[2];
108933             DistanceToPoint.computeDistance(poly.getExteriorRing(), pt$1, ptDist$1);
108934             for (var i$1 = 0; i$1 < poly.getNumInteriorRing(); i$1++) {
108935               DistanceToPoint.computeDistance(poly.getInteriorRingN(i$1), pt$1, ptDist$1);
108936             }
108937           } else if (arguments[2] instanceof PointPairDistance$2 && (arguments[0] instanceof Geometry && arguments[1] instanceof Coordinate)) {
108938             var geom = arguments[0];
108939             var pt$2 = arguments[1];
108940             var ptDist$2 = arguments[2];
108941             if (geom instanceof LineString) {
108942               DistanceToPoint.computeDistance(geom, pt$2, ptDist$2);
108943             } else if (geom instanceof Polygon) {
108944               DistanceToPoint.computeDistance(geom, pt$2, ptDist$2);
108945             } else if (geom instanceof GeometryCollection) {
108946               var gc = geom;
108947               for (var i$2 = 0; i$2 < gc.getNumGeometries(); i$2++) {
108948                 var g = gc.getGeometryN(i$2);
108949                 DistanceToPoint.computeDistance(g, pt$2, ptDist$2);
108950               }
108951             } else {
108952               ptDist$2.setMinimum(geom.getCoordinate(), pt$2);
108953             }
108954           } else if (arguments[2] instanceof PointPairDistance$2 && (arguments[0] instanceof LineSegment && arguments[1] instanceof Coordinate)) {
108955             var segment = arguments[0];
108956             var pt$3 = arguments[1];
108957             var ptDist$3 = arguments[2];
108958             var closestPt$1 = segment.closestPoint(pt$3);
108959             ptDist$3.setMinimum(closestPt$1, pt$3);
108960           }
108961         };
108962
108963         var DiscreteHausdorffDistance = function DiscreteHausdorffDistance () {
108964           this._g0 = null;
108965           this._g1 = null;
108966           this._ptDist = new PointPairDistance$2();
108967           this._densifyFrac = 0.0;
108968           var g0 = arguments[0];
108969           var g1 = arguments[1];
108970           this._g0 = g0;
108971           this._g1 = g1;
108972         };
108973
108974         var staticAccessors$39 = { MaxPointDistanceFilter: { configurable: true },MaxDensifiedByFractionDistanceFilter: { configurable: true } };
108975         DiscreteHausdorffDistance.prototype.getCoordinates = function getCoordinates () {
108976           return this._ptDist.getCoordinates()
108977         };
108978         DiscreteHausdorffDistance.prototype.setDensifyFraction = function setDensifyFraction (densifyFrac) {
108979           if (densifyFrac > 1.0 || densifyFrac <= 0.0) { throw new IllegalArgumentException('Fraction is not in range (0.0 - 1.0]') }
108980           this._densifyFrac = densifyFrac;
108981         };
108982         DiscreteHausdorffDistance.prototype.compute = function compute (g0, g1) {
108983           this.computeOrientedDistance(g0, g1, this._ptDist);
108984           this.computeOrientedDistance(g1, g0, this._ptDist);
108985         };
108986         DiscreteHausdorffDistance.prototype.distance = function distance () {
108987           this.compute(this._g0, this._g1);
108988           return this._ptDist.getDistance()
108989         };
108990         DiscreteHausdorffDistance.prototype.computeOrientedDistance = function computeOrientedDistance (discreteGeom, geom, ptDist) {
108991           var distFilter = new MaxPointDistanceFilter$1(geom);
108992           discreteGeom.apply(distFilter);
108993           ptDist.setMaximum(distFilter.getMaxPointDistance());
108994           if (this._densifyFrac > 0) {
108995             var fracFilter = new MaxDensifiedByFractionDistanceFilter(geom, this._densifyFrac);
108996             discreteGeom.apply(fracFilter);
108997             ptDist.setMaximum(fracFilter.getMaxPointDistance());
108998           }
108999         };
109000         DiscreteHausdorffDistance.prototype.orientedDistance = function orientedDistance () {
109001           this.computeOrientedDistance(this._g0, this._g1, this._ptDist);
109002           return this._ptDist.getDistance()
109003         };
109004         DiscreteHausdorffDistance.prototype.interfaces_ = function interfaces_ () {
109005           return []
109006         };
109007         DiscreteHausdorffDistance.prototype.getClass = function getClass () {
109008           return DiscreteHausdorffDistance
109009         };
109010         DiscreteHausdorffDistance.distance = function distance () {
109011           if (arguments.length === 2) {
109012             var g0 = arguments[0];
109013             var g1 = arguments[1];
109014             var dist = new DiscreteHausdorffDistance(g0, g1);
109015             return dist.distance()
109016           } else if (arguments.length === 3) {
109017             var g0$1 = arguments[0];
109018             var g1$1 = arguments[1];
109019             var densifyFrac = arguments[2];
109020             var dist$1 = new DiscreteHausdorffDistance(g0$1, g1$1);
109021             dist$1.setDensifyFraction(densifyFrac);
109022             return dist$1.distance()
109023           }
109024         };
109025         staticAccessors$39.MaxPointDistanceFilter.get = function () { return MaxPointDistanceFilter$1 };
109026         staticAccessors$39.MaxDensifiedByFractionDistanceFilter.get = function () { return MaxDensifiedByFractionDistanceFilter };
109027
109028         Object.defineProperties( DiscreteHausdorffDistance, staticAccessors$39 );
109029
109030         var MaxPointDistanceFilter$1 = function MaxPointDistanceFilter () {
109031           this._maxPtDist = new PointPairDistance$2();
109032           this._minPtDist = new PointPairDistance$2();
109033           this._euclideanDist = new DistanceToPoint();
109034           this._geom = null;
109035           var geom = arguments[0];
109036           this._geom = geom;
109037         };
109038         MaxPointDistanceFilter$1.prototype.filter = function filter (pt) {
109039           this._minPtDist.initialize();
109040           DistanceToPoint.computeDistance(this._geom, pt, this._minPtDist);
109041           this._maxPtDist.setMaximum(this._minPtDist);
109042         };
109043         MaxPointDistanceFilter$1.prototype.getMaxPointDistance = function getMaxPointDistance () {
109044           return this._maxPtDist
109045         };
109046         MaxPointDistanceFilter$1.prototype.interfaces_ = function interfaces_ () {
109047           return [CoordinateFilter]
109048         };
109049         MaxPointDistanceFilter$1.prototype.getClass = function getClass () {
109050           return MaxPointDistanceFilter$1
109051         };
109052
109053         var MaxDensifiedByFractionDistanceFilter = function MaxDensifiedByFractionDistanceFilter () {
109054           this._maxPtDist = new PointPairDistance$2();
109055           this._minPtDist = new PointPairDistance$2();
109056           this._geom = null;
109057           this._numSubSegs = 0;
109058           var geom = arguments[0];
109059           var fraction = arguments[1];
109060           this._geom = geom;
109061           this._numSubSegs = Math.trunc(Math.round(1.0 / fraction));
109062         };
109063         MaxDensifiedByFractionDistanceFilter.prototype.filter = function filter (seq, index) {
109064             var this$1 = this;
109065
109066           if (index === 0) { return null }
109067           var p0 = seq.getCoordinate(index - 1);
109068           var p1 = seq.getCoordinate(index);
109069           var delx = (p1.x - p0.x) / this._numSubSegs;
109070           var dely = (p1.y - p0.y) / this._numSubSegs;
109071           for (var i = 0; i < this._numSubSegs; i++) {
109072             var x = p0.x + i * delx;
109073             var y = p0.y + i * dely;
109074             var pt = new Coordinate(x, y);
109075             this$1._minPtDist.initialize();
109076             DistanceToPoint.computeDistance(this$1._geom, pt, this$1._minPtDist);
109077             this$1._maxPtDist.setMaximum(this$1._minPtDist);
109078           }
109079         };
109080         MaxDensifiedByFractionDistanceFilter.prototype.isDone = function isDone () {
109081           return false
109082         };
109083         MaxDensifiedByFractionDistanceFilter.prototype.isGeometryChanged = function isGeometryChanged () {
109084           return false
109085         };
109086         MaxDensifiedByFractionDistanceFilter.prototype.getMaxPointDistance = function getMaxPointDistance () {
109087           return this._maxPtDist
109088         };
109089         MaxDensifiedByFractionDistanceFilter.prototype.interfaces_ = function interfaces_ () {
109090           return [CoordinateSequenceFilter]
109091         };
109092         MaxDensifiedByFractionDistanceFilter.prototype.getClass = function getClass () {
109093           return MaxDensifiedByFractionDistanceFilter
109094         };
109095
109096         var BufferDistanceValidator = function BufferDistanceValidator (input, bufDistance, result) {
109097           this._minValidDistance = null;
109098           this._maxValidDistance = null;
109099           this._minDistanceFound = null;
109100           this._maxDistanceFound = null;
109101           this._isValid = true;
109102           this._errMsg = null;
109103           this._errorLocation = null;
109104           this._errorIndicator = null;
109105           this._input = input || null;
109106           this._bufDistance = bufDistance || null;
109107           this._result = result || null;
109108         };
109109
109110         var staticAccessors$37 = { VERBOSE: { configurable: true },MAX_DISTANCE_DIFF_FRAC: { configurable: true } };
109111         BufferDistanceValidator.prototype.checkMaximumDistance = function checkMaximumDistance (input, bufCurve, maxDist) {
109112           var haus = new DiscreteHausdorffDistance(bufCurve, input);
109113           haus.setDensifyFraction(0.25);
109114           this._maxDistanceFound = haus.orientedDistance();
109115           if (this._maxDistanceFound > maxDist) {
109116             this._isValid = false;
109117             var pts = haus.getCoordinates();
109118             this._errorLocation = pts[1];
109119             this._errorIndicator = input.getFactory().createLineString(pts);
109120             this._errMsg = 'Distance between buffer curve and input is too large (' + this._maxDistanceFound + ' at ' + WKTWriter.toLineString(pts[0], pts[1]) + ')';
109121           }
109122         };
109123         BufferDistanceValidator.prototype.isValid = function isValid () {
109124           var posDistance = Math.abs(this._bufDistance);
109125           var distDelta = BufferDistanceValidator.MAX_DISTANCE_DIFF_FRAC * posDistance;
109126           this._minValidDistance = posDistance - distDelta;
109127           this._maxValidDistance = posDistance + distDelta;
109128           if (this._input.isEmpty() || this._result.isEmpty()) { return true }
109129           if (this._bufDistance > 0.0) {
109130             this.checkPositiveValid();
109131           } else {
109132             this.checkNegativeValid();
109133           }
109134           if (BufferDistanceValidator.VERBOSE) {
109135             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));
109136           }
109137           return this._isValid
109138         };
109139         BufferDistanceValidator.prototype.checkNegativeValid = function checkNegativeValid () {
109140           if (!(this._input instanceof Polygon || this._input instanceof MultiPolygon || this._input instanceof GeometryCollection)) {
109141             return null
109142           }
109143           var inputCurve = this.getPolygonLines(this._input);
109144           this.checkMinimumDistance(inputCurve, this._result, this._minValidDistance);
109145           if (!this._isValid) { return null }
109146           this.checkMaximumDistance(inputCurve, this._result, this._maxValidDistance);
109147         };
109148         BufferDistanceValidator.prototype.getErrorIndicator = function getErrorIndicator () {
109149           return this._errorIndicator
109150         };
109151         BufferDistanceValidator.prototype.checkMinimumDistance = function checkMinimumDistance (g1, g2, minDist) {
109152           var distOp = new DistanceOp(g1, g2, minDist);
109153           this._minDistanceFound = distOp.distance();
109154           if (this._minDistanceFound < minDist) {
109155             this._isValid = false;
109156             var pts = distOp.nearestPoints();
109157             this._errorLocation = distOp.nearestPoints()[1];
109158             this._errorIndicator = g1.getFactory().createLineString(pts);
109159             this._errMsg = 'Distance between buffer curve and input is too small (' + this._minDistanceFound + ' at ' + WKTWriter.toLineString(pts[0], pts[1]) + ' )';
109160           }
109161         };
109162         BufferDistanceValidator.prototype.checkPositiveValid = function checkPositiveValid () {
109163           var bufCurve = this._result.getBoundary();
109164           this.checkMinimumDistance(this._input, bufCurve, this._minValidDistance);
109165           if (!this._isValid) { return null }
109166           this.checkMaximumDistance(this._input, bufCurve, this._maxValidDistance);
109167         };
109168         BufferDistanceValidator.prototype.getErrorLocation = function getErrorLocation () {
109169           return this._errorLocation
109170         };
109171         BufferDistanceValidator.prototype.getPolygonLines = function getPolygonLines (g) {
109172           var lines = new ArrayList();
109173           var lineExtracter = new LinearComponentExtracter(lines);
109174           var polys = PolygonExtracter.getPolygons(g);
109175           for (var i = polys.iterator(); i.hasNext();) {
109176             var poly = i.next();
109177             poly.apply(lineExtracter);
109178           }
109179           return g.getFactory().buildGeometry(lines)
109180         };
109181         BufferDistanceValidator.prototype.getErrorMessage = function getErrorMessage () {
109182           return this._errMsg
109183         };
109184         BufferDistanceValidator.prototype.interfaces_ = function interfaces_ () {
109185           return []
109186         };
109187         BufferDistanceValidator.prototype.getClass = function getClass () {
109188           return BufferDistanceValidator
109189         };
109190         staticAccessors$37.VERBOSE.get = function () { return false };
109191         staticAccessors$37.MAX_DISTANCE_DIFF_FRAC.get = function () { return 0.012 };
109192
109193         Object.defineProperties( BufferDistanceValidator, staticAccessors$37 );
109194
109195         var BufferResultValidator = function BufferResultValidator (input, distance, result) {
109196           this._isValid = true;
109197           this._errorMsg = null;
109198           this._errorLocation = null;
109199           this._errorIndicator = null;
109200           this._input = input || null;
109201           this._distance = distance || null;
109202           this._result = result || null;
109203         };
109204
109205         var staticAccessors$40 = { VERBOSE: { configurable: true },MAX_ENV_DIFF_FRAC: { configurable: true } };
109206         BufferResultValidator.prototype.isValid = function isValid () {
109207           this.checkPolygonal();
109208           if (!this._isValid) { return this._isValid }
109209           this.checkExpectedEmpty();
109210           if (!this._isValid) { return this._isValid }
109211           this.checkEnvelope();
109212           if (!this._isValid) { return this._isValid }
109213           this.checkArea();
109214           if (!this._isValid) { return this._isValid }
109215           this.checkDistance();
109216           return this._isValid
109217         };
109218         BufferResultValidator.prototype.checkEnvelope = function checkEnvelope () {
109219           if (this._distance < 0.0) { return null }
109220           var padding = this._distance * BufferResultValidator.MAX_ENV_DIFF_FRAC;
109221           if (padding === 0.0) { padding = 0.001; }
109222           var expectedEnv = new Envelope(this._input.getEnvelopeInternal());
109223           expectedEnv.expandBy(this._distance);
109224           var bufEnv = new Envelope(this._result.getEnvelopeInternal());
109225           bufEnv.expandBy(padding);
109226           if (!bufEnv.contains(expectedEnv)) {
109227             this._isValid = false;
109228             this._errorMsg = 'Buffer envelope is incorrect';
109229             this._errorIndicator = this._input.getFactory().toGeometry(bufEnv);
109230           }
109231           this.report('Envelope');
109232         };
109233         BufferResultValidator.prototype.checkDistance = function checkDistance () {
109234           var distValid = new BufferDistanceValidator(this._input, this._distance, this._result);
109235           if (!distValid.isValid()) {
109236             this._isValid = false;
109237             this._errorMsg = distValid.getErrorMessage();
109238             this._errorLocation = distValid.getErrorLocation();
109239             this._errorIndicator = distValid.getErrorIndicator();
109240           }
109241           this.report('Distance');
109242         };
109243         BufferResultValidator.prototype.checkArea = function checkArea () {
109244           var inputArea = this._input.getArea();
109245           var resultArea = this._result.getArea();
109246           if (this._distance > 0.0 && inputArea > resultArea) {
109247             this._isValid = false;
109248             this._errorMsg = 'Area of positive buffer is smaller than input';
109249             this._errorIndicator = this._result;
109250           }
109251           if (this._distance < 0.0 && inputArea < resultArea) {
109252             this._isValid = false;
109253             this._errorMsg = 'Area of negative buffer is larger than input';
109254             this._errorIndicator = this._result;
109255           }
109256           this.report('Area');
109257         };
109258         BufferResultValidator.prototype.checkPolygonal = function checkPolygonal () {
109259           if (!(this._result instanceof Polygon || this._result instanceof MultiPolygon)) { this._isValid = false; }
109260           this._errorMsg = 'Result is not polygonal';
109261           this._errorIndicator = this._result;
109262           this.report('Polygonal');
109263         };
109264         BufferResultValidator.prototype.getErrorIndicator = function getErrorIndicator () {
109265           return this._errorIndicator
109266         };
109267         BufferResultValidator.prototype.getErrorLocation = function getErrorLocation () {
109268           return this._errorLocation
109269         };
109270         BufferResultValidator.prototype.checkExpectedEmpty = function checkExpectedEmpty () {
109271           if (this._input.getDimension() >= 2) { return null }
109272           if (this._distance > 0.0) { return null }
109273           if (!this._result.isEmpty()) {
109274             this._isValid = false;
109275             this._errorMsg = 'Result is non-empty';
109276             this._errorIndicator = this._result;
109277           }
109278           this.report('ExpectedEmpty');
109279         };
109280         BufferResultValidator.prototype.report = function report (checkName) {
109281           if (!BufferResultValidator.VERBOSE) { return null }
109282           System.out.println('Check ' + checkName + ': ' + (this._isValid ? 'passed' : 'FAILED'));
109283         };
109284         BufferResultValidator.prototype.getErrorMessage = function getErrorMessage () {
109285           return this._errorMsg
109286         };
109287         BufferResultValidator.prototype.interfaces_ = function interfaces_ () {
109288           return []
109289         };
109290         BufferResultValidator.prototype.getClass = function getClass () {
109291           return BufferResultValidator
109292         };
109293         BufferResultValidator.isValidMsg = function isValidMsg (g, distance, result) {
109294           var validator = new BufferResultValidator(g, distance, result);
109295           if (!validator.isValid()) { return validator.getErrorMessage() }
109296           return null
109297         };
109298         BufferResultValidator.isValid = function isValid (g, distance, result) {
109299           var validator = new BufferResultValidator(g, distance, result);
109300           if (validator.isValid()) { return true }
109301           return false
109302         };
109303         staticAccessors$40.VERBOSE.get = function () { return false };
109304         staticAccessors$40.MAX_ENV_DIFF_FRAC.get = function () { return 0.012 };
109305
109306         Object.defineProperties( BufferResultValidator, staticAccessors$40 );
109307
109308         // operation.buffer
109309
109310         var BasicSegmentString = function BasicSegmentString () {
109311           this._pts = null;
109312           this._data = null;
109313           var pts = arguments[0];
109314           var data = arguments[1];
109315           this._pts = pts;
109316           this._data = data;
109317         };
109318         BasicSegmentString.prototype.getCoordinates = function getCoordinates () {
109319           return this._pts
109320         };
109321         BasicSegmentString.prototype.size = function size () {
109322           return this._pts.length
109323         };
109324         BasicSegmentString.prototype.getCoordinate = function getCoordinate (i) {
109325           return this._pts[i]
109326         };
109327         BasicSegmentString.prototype.isClosed = function isClosed () {
109328           return this._pts[0].equals(this._pts[this._pts.length - 1])
109329         };
109330         BasicSegmentString.prototype.getSegmentOctant = function getSegmentOctant (index) {
109331           if (index === this._pts.length - 1) { return -1 }
109332           return Octant.octant(this.getCoordinate(index), this.getCoordinate(index + 1))
109333         };
109334         BasicSegmentString.prototype.setData = function setData (data) {
109335           this._data = data;
109336         };
109337         BasicSegmentString.prototype.getData = function getData () {
109338           return this._data
109339         };
109340         BasicSegmentString.prototype.toString = function toString () {
109341           return WKTWriter.toLineString(new CoordinateArraySequence(this._pts))
109342         };
109343         BasicSegmentString.prototype.interfaces_ = function interfaces_ () {
109344           return [SegmentString]
109345         };
109346         BasicSegmentString.prototype.getClass = function getClass () {
109347           return BasicSegmentString
109348         };
109349
109350         var InteriorIntersectionFinder = function InteriorIntersectionFinder () {
109351           this._findAllIntersections = false;
109352           this._isCheckEndSegmentsOnly = false;
109353           this._li = null;
109354           this._interiorIntersection = null;
109355           this._intSegments = null;
109356           this._intersections = new ArrayList();
109357           this._intersectionCount = 0;
109358           this._keepIntersections = true;
109359           var li = arguments[0];
109360           this._li = li;
109361           this._interiorIntersection = null;
109362         };
109363         InteriorIntersectionFinder.prototype.getInteriorIntersection = function getInteriorIntersection () {
109364           return this._interiorIntersection
109365         };
109366         InteriorIntersectionFinder.prototype.setCheckEndSegmentsOnly = function setCheckEndSegmentsOnly (isCheckEndSegmentsOnly) {
109367           this._isCheckEndSegmentsOnly = isCheckEndSegmentsOnly;
109368         };
109369         InteriorIntersectionFinder.prototype.getIntersectionSegments = function getIntersectionSegments () {
109370           return this._intSegments
109371         };
109372         InteriorIntersectionFinder.prototype.count = function count () {
109373           return this._intersectionCount
109374         };
109375         InteriorIntersectionFinder.prototype.getIntersections = function getIntersections () {
109376           return this._intersections
109377         };
109378         InteriorIntersectionFinder.prototype.setFindAllIntersections = function setFindAllIntersections (findAllIntersections) {
109379           this._findAllIntersections = findAllIntersections;
109380         };
109381         InteriorIntersectionFinder.prototype.setKeepIntersections = function setKeepIntersections (keepIntersections) {
109382           this._keepIntersections = keepIntersections;
109383         };
109384         InteriorIntersectionFinder.prototype.processIntersections = function processIntersections (e0, segIndex0, e1, segIndex1) {
109385           if (!this._findAllIntersections && this.hasIntersection()) { return null }
109386           if (e0 === e1 && segIndex0 === segIndex1) { return null }
109387           if (this._isCheckEndSegmentsOnly) {
109388             var isEndSegPresent = this.isEndSegment(e0, segIndex0) || this.isEndSegment(e1, segIndex1);
109389             if (!isEndSegPresent) { return null }
109390           }
109391           var p00 = e0.getCoordinates()[segIndex0];
109392           var p01 = e0.getCoordinates()[segIndex0 + 1];
109393           var p10 = e1.getCoordinates()[segIndex1];
109394           var p11 = e1.getCoordinates()[segIndex1 + 1];
109395           this._li.computeIntersection(p00, p01, p10, p11);
109396           if (this._li.hasIntersection()) {
109397             if (this._li.isInteriorIntersection()) {
109398               this._intSegments = new Array(4).fill(null);
109399               this._intSegments[0] = p00;
109400               this._intSegments[1] = p01;
109401               this._intSegments[2] = p10;
109402               this._intSegments[3] = p11;
109403               this._interiorIntersection = this._li.getIntersection(0);
109404               if (this._keepIntersections) { this._intersections.add(this._interiorIntersection); }
109405               this._intersectionCount++;
109406             }
109407           }
109408         };
109409         InteriorIntersectionFinder.prototype.isEndSegment = function isEndSegment (segStr, index) {
109410           if (index === 0) { return true }
109411           if (index >= segStr.size() - 2) { return true }
109412           return false
109413         };
109414         InteriorIntersectionFinder.prototype.hasIntersection = function hasIntersection () {
109415           return this._interiorIntersection !== null
109416         };
109417         InteriorIntersectionFinder.prototype.isDone = function isDone () {
109418           if (this._findAllIntersections) { return false }
109419           return this._interiorIntersection !== null
109420         };
109421         InteriorIntersectionFinder.prototype.interfaces_ = function interfaces_ () {
109422           return [SegmentIntersector]
109423         };
109424         InteriorIntersectionFinder.prototype.getClass = function getClass () {
109425           return InteriorIntersectionFinder
109426         };
109427         InteriorIntersectionFinder.createAllIntersectionsFinder = function createAllIntersectionsFinder (li) {
109428           var finder = new InteriorIntersectionFinder(li);
109429           finder.setFindAllIntersections(true);
109430           return finder
109431         };
109432         InteriorIntersectionFinder.createAnyIntersectionFinder = function createAnyIntersectionFinder (li) {
109433           return new InteriorIntersectionFinder(li)
109434         };
109435         InteriorIntersectionFinder.createIntersectionCounter = function createIntersectionCounter (li) {
109436           var finder = new InteriorIntersectionFinder(li);
109437           finder.setFindAllIntersections(true);
109438           finder.setKeepIntersections(false);
109439           return finder
109440         };
109441
109442         var FastNodingValidator = function FastNodingValidator () {
109443           this._li = new RobustLineIntersector();
109444           this._segStrings = null;
109445           this._findAllIntersections = false;
109446           this._segInt = null;
109447           this._isValid = true;
109448           var segStrings = arguments[0];
109449           this._segStrings = segStrings;
109450         };
109451         FastNodingValidator.prototype.execute = function execute () {
109452           if (this._segInt !== null) { return null }
109453           this.checkInteriorIntersections();
109454         };
109455         FastNodingValidator.prototype.getIntersections = function getIntersections () {
109456           return this._segInt.getIntersections()
109457         };
109458         FastNodingValidator.prototype.isValid = function isValid () {
109459           this.execute();
109460           return this._isValid
109461         };
109462         FastNodingValidator.prototype.setFindAllIntersections = function setFindAllIntersections (findAllIntersections) {
109463           this._findAllIntersections = findAllIntersections;
109464         };
109465         FastNodingValidator.prototype.checkInteriorIntersections = function checkInteriorIntersections () {
109466           this._isValid = true;
109467           this._segInt = new InteriorIntersectionFinder(this._li);
109468           this._segInt.setFindAllIntersections(this._findAllIntersections);
109469           var noder = new MCIndexNoder();
109470           noder.setSegmentIntersector(this._segInt);
109471           noder.computeNodes(this._segStrings);
109472           if (this._segInt.hasIntersection()) {
109473             this._isValid = false;
109474             return null
109475           }
109476         };
109477         FastNodingValidator.prototype.checkValid = function checkValid () {
109478           this.execute();
109479           if (!this._isValid) { throw new TopologyException(this.getErrorMessage(), this._segInt.getInteriorIntersection()) }
109480         };
109481         FastNodingValidator.prototype.getErrorMessage = function getErrorMessage () {
109482           if (this._isValid) { return 'no intersections found' }
109483           var intSegs = this._segInt.getIntersectionSegments();
109484           return 'found non-noded intersection between ' + WKTWriter.toLineString(intSegs[0], intSegs[1]) + ' and ' + WKTWriter.toLineString(intSegs[2], intSegs[3])
109485         };
109486         FastNodingValidator.prototype.interfaces_ = function interfaces_ () {
109487           return []
109488         };
109489         FastNodingValidator.prototype.getClass = function getClass () {
109490           return FastNodingValidator
109491         };
109492         FastNodingValidator.computeIntersections = function computeIntersections (segStrings) {
109493           var nv = new FastNodingValidator(segStrings);
109494           nv.setFindAllIntersections(true);
109495           nv.isValid();
109496           return nv.getIntersections()
109497         };
109498
109499         var EdgeNodingValidator = function EdgeNodingValidator () {
109500           this._nv = null;
109501           var edges = arguments[0];
109502           this._nv = new FastNodingValidator(EdgeNodingValidator.toSegmentStrings(edges));
109503         };
109504         EdgeNodingValidator.prototype.checkValid = function checkValid () {
109505           this._nv.checkValid();
109506         };
109507         EdgeNodingValidator.prototype.interfaces_ = function interfaces_ () {
109508           return []
109509         };
109510         EdgeNodingValidator.prototype.getClass = function getClass () {
109511           return EdgeNodingValidator
109512         };
109513         EdgeNodingValidator.toSegmentStrings = function toSegmentStrings (edges) {
109514           var segStrings = new ArrayList();
109515           for (var i = edges.iterator(); i.hasNext();) {
109516             var e = i.next();
109517             segStrings.add(new BasicSegmentString(e.getCoordinates(), e));
109518           }
109519           return segStrings
109520         };
109521         EdgeNodingValidator.checkValid = function checkValid (edges) {
109522           var validator = new EdgeNodingValidator(edges);
109523           validator.checkValid();
109524         };
109525
109526         var GeometryCollectionMapper = function GeometryCollectionMapper (mapOp) {
109527           this._mapOp = mapOp;
109528         };
109529         GeometryCollectionMapper.prototype.map = function map (gc) {
109530             var this$1 = this;
109531
109532           var mapped = new ArrayList();
109533           for (var i = 0; i < gc.getNumGeometries(); i++) {
109534             var g = this$1._mapOp.map(gc.getGeometryN(i));
109535             if (!g.isEmpty()) { mapped.add(g); }
109536           }
109537           return gc.getFactory().createGeometryCollection(GeometryFactory.toGeometryArray(mapped))
109538         };
109539         GeometryCollectionMapper.prototype.interfaces_ = function interfaces_ () {
109540           return []
109541         };
109542         GeometryCollectionMapper.prototype.getClass = function getClass () {
109543           return GeometryCollectionMapper
109544         };
109545         GeometryCollectionMapper.map = function map (gc, op) {
109546           var mapper = new GeometryCollectionMapper(op);
109547           return mapper.map(gc)
109548         };
109549
109550         var LineBuilder = function LineBuilder () {
109551           this._op = null;
109552           this._geometryFactory = null;
109553           this._ptLocator = null;
109554           this._lineEdgesList = new ArrayList();
109555           this._resultLineList = new ArrayList();
109556           var op = arguments[0];
109557           var geometryFactory = arguments[1];
109558           var ptLocator = arguments[2];
109559           this._op = op;
109560           this._geometryFactory = geometryFactory;
109561           this._ptLocator = ptLocator;
109562         };
109563         LineBuilder.prototype.collectLines = function collectLines (opCode) {
109564             var this$1 = this;
109565
109566           for (var it = this._op.getGraph().getEdgeEnds().iterator(); it.hasNext();) {
109567             var de = it.next();
109568             this$1.collectLineEdge(de, opCode, this$1._lineEdgesList);
109569             this$1.collectBoundaryTouchEdge(de, opCode, this$1._lineEdgesList);
109570           }
109571         };
109572         LineBuilder.prototype.labelIsolatedLine = function labelIsolatedLine (e, targetIndex) {
109573           var loc = this._ptLocator.locate(e.getCoordinate(), this._op.getArgGeometry(targetIndex));
109574           e.getLabel().setLocation(targetIndex, loc);
109575         };
109576         LineBuilder.prototype.build = function build (opCode) {
109577           this.findCoveredLineEdges();
109578           this.collectLines(opCode);
109579           this.buildLines(opCode);
109580           return this._resultLineList
109581         };
109582         LineBuilder.prototype.collectLineEdge = function collectLineEdge (de, opCode, edges) {
109583           var label = de.getLabel();
109584           var e = de.getEdge();
109585           if (de.isLineEdge()) {
109586             if (!de.isVisited() && OverlayOp.isResultOfOp(label, opCode) && !e.isCovered()) {
109587               edges.add(e);
109588               de.setVisitedEdge(true);
109589             }
109590           }
109591         };
109592         LineBuilder.prototype.findCoveredLineEdges = function findCoveredLineEdges () {
109593             var this$1 = this;
109594
109595           for (var nodeit = this._op.getGraph().getNodes().iterator(); nodeit.hasNext();) {
109596             var node = nodeit.next();
109597             node.getEdges().findCoveredLineEdges();
109598           }
109599           for (var it = this._op.getGraph().getEdgeEnds().iterator(); it.hasNext();) {
109600             var de = it.next();
109601             var e = de.getEdge();
109602             if (de.isLineEdge() && !e.isCoveredSet()) {
109603               var isCovered = this$1._op.isCoveredByA(de.getCoordinate());
109604               e.setCovered(isCovered);
109605             }
109606           }
109607         };
109608         LineBuilder.prototype.labelIsolatedLines = function labelIsolatedLines (edgesList) {
109609             var this$1 = this;
109610
109611           for (var it = edgesList.iterator(); it.hasNext();) {
109612             var e = it.next();
109613             var label = e.getLabel();
109614             if (e.isIsolated()) {
109615               if (label.isNull(0)) { this$1.labelIsolatedLine(e, 0); } else { this$1.labelIsolatedLine(e, 1); }
109616             }
109617           }
109618         };
109619         LineBuilder.prototype.buildLines = function buildLines (opCode) {
109620             var this$1 = this;
109621
109622           for (var it = this._lineEdgesList.iterator(); it.hasNext();) {
109623             var e = it.next();
109624             // const label = e.getLabel()
109625             var line = this$1._geometryFactory.createLineString(e.getCoordinates());
109626             this$1._resultLineList.add(line);
109627             e.setInResult(true);
109628           }
109629         };
109630         LineBuilder.prototype.collectBoundaryTouchEdge = function collectBoundaryTouchEdge (de, opCode, edges) {
109631           var label = de.getLabel();
109632           if (de.isLineEdge()) { return null }
109633           if (de.isVisited()) { return null }
109634           if (de.isInteriorAreaEdge()) { return null }
109635           if (de.getEdge().isInResult()) { return null }
109636           Assert.isTrue(!(de.isInResult() || de.getSym().isInResult()) || !de.getEdge().isInResult());
109637           if (OverlayOp.isResultOfOp(label, opCode) && opCode === OverlayOp.INTERSECTION) {
109638             edges.add(de.getEdge());
109639             de.setVisitedEdge(true);
109640           }
109641         };
109642         LineBuilder.prototype.interfaces_ = function interfaces_ () {
109643           return []
109644         };
109645         LineBuilder.prototype.getClass = function getClass () {
109646           return LineBuilder
109647         };
109648
109649         var PointBuilder = function PointBuilder () {
109650           this._op = null;
109651           this._geometryFactory = null;
109652           this._resultPointList = new ArrayList();
109653           var op = arguments[0];
109654           var geometryFactory = arguments[1];
109655           // const ptLocator = arguments[2]
109656           this._op = op;
109657           this._geometryFactory = geometryFactory;
109658         };
109659         PointBuilder.prototype.filterCoveredNodeToPoint = function filterCoveredNodeToPoint (n) {
109660           var coord = n.getCoordinate();
109661           if (!this._op.isCoveredByLA(coord)) {
109662             var pt = this._geometryFactory.createPoint(coord);
109663             this._resultPointList.add(pt);
109664           }
109665         };
109666         PointBuilder.prototype.extractNonCoveredResultNodes = function extractNonCoveredResultNodes (opCode) {
109667             var this$1 = this;
109668
109669           for (var nodeit = this._op.getGraph().getNodes().iterator(); nodeit.hasNext();) {
109670             var n = nodeit.next();
109671             if (n.isInResult()) { continue }
109672             if (n.isIncidentEdgeInResult()) { continue }
109673             if (n.getEdges().getDegree() === 0 || opCode === OverlayOp.INTERSECTION) {
109674               var label = n.getLabel();
109675               if (OverlayOp.isResultOfOp(label, opCode)) {
109676                 this$1.filterCoveredNodeToPoint(n);
109677               }
109678             }
109679           }
109680         };
109681         PointBuilder.prototype.build = function build (opCode) {
109682           this.extractNonCoveredResultNodes(opCode);
109683           return this._resultPointList
109684         };
109685         PointBuilder.prototype.interfaces_ = function interfaces_ () {
109686           return []
109687         };
109688         PointBuilder.prototype.getClass = function getClass () {
109689           return PointBuilder
109690         };
109691
109692         var GeometryTransformer = function GeometryTransformer () {
109693           this._inputGeom = null;
109694           this._factory = null;
109695           this._pruneEmptyGeometry = true;
109696           this._preserveGeometryCollectionType = true;
109697           this._preserveCollections = false;
109698           this._preserveType = false;
109699         };
109700         GeometryTransformer.prototype.transformPoint = function transformPoint (geom, parent) {
109701           return this._factory.createPoint(this.transformCoordinates(geom.getCoordinateSequence(), geom))
109702         };
109703         GeometryTransformer.prototype.transformPolygon = function transformPolygon (geom, parent) {
109704             var this$1 = this;
109705
109706           var isAllValidLinearRings = true;
109707           var shell = this.transformLinearRing(geom.getExteriorRing(), geom);
109708           if (shell === null || !(shell instanceof LinearRing) || shell.isEmpty()) { isAllValidLinearRings = false; }
109709           var holes = new ArrayList();
109710           for (var i = 0; i < geom.getNumInteriorRing(); i++) {
109711             var hole = this$1.transformLinearRing(geom.getInteriorRingN(i), geom);
109712             if (hole === null || hole.isEmpty()) {
109713               continue
109714             }
109715             if (!(hole instanceof LinearRing)) { isAllValidLinearRings = false; }
109716             holes.add(hole);
109717           }
109718           if (isAllValidLinearRings) { return this._factory.createPolygon(shell, holes.toArray([])); } else {
109719             var components = new ArrayList();
109720             if (shell !== null) { components.add(shell); }
109721             components.addAll(holes);
109722             return this._factory.buildGeometry(components)
109723           }
109724         };
109725         GeometryTransformer.prototype.createCoordinateSequence = function createCoordinateSequence (coords) {
109726           return this._factory.getCoordinateSequenceFactory().create(coords)
109727         };
109728         GeometryTransformer.prototype.getInputGeometry = function getInputGeometry () {
109729           return this._inputGeom
109730         };
109731         GeometryTransformer.prototype.transformMultiLineString = function transformMultiLineString (geom, parent) {
109732             var this$1 = this;
109733
109734           var transGeomList = new ArrayList();
109735           for (var i = 0; i < geom.getNumGeometries(); i++) {
109736             var transformGeom = this$1.transformLineString(geom.getGeometryN(i), geom);
109737             if (transformGeom === null) { continue }
109738             if (transformGeom.isEmpty()) { continue }
109739             transGeomList.add(transformGeom);
109740           }
109741           return this._factory.buildGeometry(transGeomList)
109742         };
109743         GeometryTransformer.prototype.transformCoordinates = function transformCoordinates (coords, parent) {
109744           return this.copy(coords)
109745         };
109746         GeometryTransformer.prototype.transformLineString = function transformLineString (geom, parent) {
109747           return this._factory.createLineString(this.transformCoordinates(geom.getCoordinateSequence(), geom))
109748         };
109749         GeometryTransformer.prototype.transformMultiPoint = function transformMultiPoint (geom, parent) {
109750             var this$1 = this;
109751
109752           var transGeomList = new ArrayList();
109753           for (var i = 0; i < geom.getNumGeometries(); i++) {
109754             var transformGeom = this$1.transformPoint(geom.getGeometryN(i), geom);
109755             if (transformGeom === null) { continue }
109756             if (transformGeom.isEmpty()) { continue }
109757             transGeomList.add(transformGeom);
109758           }
109759           return this._factory.buildGeometry(transGeomList)
109760         };
109761         GeometryTransformer.prototype.transformMultiPolygon = function transformMultiPolygon (geom, parent) {
109762             var this$1 = this;
109763
109764           var transGeomList = new ArrayList();
109765           for (var i = 0; i < geom.getNumGeometries(); i++) {
109766             var transformGeom = this$1.transformPolygon(geom.getGeometryN(i), geom);
109767             if (transformGeom === null) { continue }
109768             if (transformGeom.isEmpty()) { continue }
109769             transGeomList.add(transformGeom);
109770           }
109771           return this._factory.buildGeometry(transGeomList)
109772         };
109773         GeometryTransformer.prototype.copy = function copy (seq) {
109774           return seq.copy()
109775         };
109776         GeometryTransformer.prototype.transformGeometryCollection = function transformGeometryCollection (geom, parent) {
109777             var this$1 = this;
109778
109779           var transGeomList = new ArrayList();
109780           for (var i = 0; i < geom.getNumGeometries(); i++) {
109781             var transformGeom = this$1.transform(geom.getGeometryN(i));
109782             if (transformGeom === null) { continue }
109783             if (this$1._pruneEmptyGeometry && transformGeom.isEmpty()) { continue }
109784             transGeomList.add(transformGeom);
109785           }
109786           if (this._preserveGeometryCollectionType) { return this._factory.createGeometryCollection(GeometryFactory.toGeometryArray(transGeomList)) }
109787           return this._factory.buildGeometry(transGeomList)
109788         };
109789         GeometryTransformer.prototype.transform = function transform (inputGeom) {
109790           this._inputGeom = inputGeom;
109791           this._factory = inputGeom.getFactory();
109792           if (inputGeom instanceof Point$1) { return this.transformPoint(inputGeom, null) }
109793           if (inputGeom instanceof MultiPoint) { return this.transformMultiPoint(inputGeom, null) }
109794           if (inputGeom instanceof LinearRing) { return this.transformLinearRing(inputGeom, null) }
109795           if (inputGeom instanceof LineString) { return this.transformLineString(inputGeom, null) }
109796           if (inputGeom instanceof MultiLineString) { return this.transformMultiLineString(inputGeom, null) }
109797           if (inputGeom instanceof Polygon) { return this.transformPolygon(inputGeom, null) }
109798           if (inputGeom instanceof MultiPolygon) { return this.transformMultiPolygon(inputGeom, null) }
109799           if (inputGeom instanceof GeometryCollection) { return this.transformGeometryCollection(inputGeom, null) }
109800           throw new IllegalArgumentException('Unknown Geometry subtype: ' + inputGeom.getClass().getName())
109801         };
109802         GeometryTransformer.prototype.transformLinearRing = function transformLinearRing (geom, parent) {
109803           var seq = this.transformCoordinates(geom.getCoordinateSequence(), geom);
109804           if (seq === null) { return this._factory.createLinearRing(null) }
109805           var seqSize = seq.size();
109806           if (seqSize > 0 && seqSize < 4 && !this._preserveType) { return this._factory.createLineString(seq) }
109807           return this._factory.createLinearRing(seq)
109808         };
109809         GeometryTransformer.prototype.interfaces_ = function interfaces_ () {
109810           return []
109811         };
109812         GeometryTransformer.prototype.getClass = function getClass () {
109813           return GeometryTransformer
109814         };
109815
109816         var LineStringSnapper = function LineStringSnapper () {
109817           this._snapTolerance = 0.0;
109818           this._srcPts = null;
109819           this._seg = new LineSegment();
109820           this._allowSnappingToSourceVertices = false;
109821           this._isClosed = false;
109822           if (arguments[0] instanceof LineString && typeof arguments[1] === 'number') {
109823             var srcLine = arguments[0];
109824             var snapTolerance = arguments[1];
109825             LineStringSnapper.call(this, srcLine.getCoordinates(), snapTolerance);
109826           } else if (arguments[0] instanceof Array && typeof arguments[1] === 'number') {
109827             var srcPts = arguments[0];
109828             var snapTolerance$1 = arguments[1];
109829             this._srcPts = srcPts;
109830             this._isClosed = LineStringSnapper.isClosed(srcPts);
109831             this._snapTolerance = snapTolerance$1;
109832           }
109833         };
109834         LineStringSnapper.prototype.snapVertices = function snapVertices (srcCoords, snapPts) {
109835             var this$1 = this;
109836
109837           var end = this._isClosed ? srcCoords.size() - 1 : srcCoords.size();
109838           for (var i = 0; i < end; i++) {
109839             var srcPt = srcCoords.get(i);
109840             var snapVert = this$1.findSnapForVertex(srcPt, snapPts);
109841             if (snapVert !== null) {
109842               srcCoords.set(i, new Coordinate(snapVert));
109843               if (i === 0 && this$1._isClosed) { srcCoords.set(srcCoords.size() - 1, new Coordinate(snapVert)); }
109844             }
109845           }
109846         };
109847         LineStringSnapper.prototype.findSnapForVertex = function findSnapForVertex (pt, snapPts) {
109848             var this$1 = this;
109849
109850           for (var i = 0; i < snapPts.length; i++) {
109851             if (pt.equals2D(snapPts[i])) { return null }
109852             if (pt.distance(snapPts[i]) < this$1._snapTolerance) { return snapPts[i] }
109853           }
109854           return null
109855         };
109856         LineStringSnapper.prototype.snapTo = function snapTo (snapPts) {
109857           var coordList = new CoordinateList(this._srcPts);
109858           this.snapVertices(coordList, snapPts);
109859           this.snapSegments(coordList, snapPts);
109860           var newPts = coordList.toCoordinateArray();
109861           return newPts
109862         };
109863         LineStringSnapper.prototype.snapSegments = function snapSegments (srcCoords, snapPts) {
109864             var this$1 = this;
109865
109866           if (snapPts.length === 0) { return null }
109867           var distinctPtCount = snapPts.length;
109868           if (snapPts[0].equals2D(snapPts[snapPts.length - 1])) { distinctPtCount = snapPts.length - 1; }
109869           for (var i = 0; i < distinctPtCount; i++) {
109870             var snapPt = snapPts[i];
109871             var index = this$1.findSegmentIndexToSnap(snapPt, srcCoords);
109872             if (index >= 0) {
109873               srcCoords.add(index + 1, new Coordinate(snapPt), false);
109874             }
109875           }
109876         };
109877         LineStringSnapper.prototype.findSegmentIndexToSnap = function findSegmentIndexToSnap (snapPt, srcCoords) {
109878             var this$1 = this;
109879
109880           var minDist = Double.MAX_VALUE;
109881           var snapIndex = -1;
109882           for (var i = 0; i < srcCoords.size() - 1; i++) {
109883             this$1._seg.p0 = srcCoords.get(i);
109884             this$1._seg.p1 = srcCoords.get(i + 1);
109885             if (this$1._seg.p0.equals2D(snapPt) || this$1._seg.p1.equals2D(snapPt)) {
109886               if (this$1._allowSnappingToSourceVertices) { continue; } else { return -1 }
109887             }
109888             var dist = this$1._seg.distance(snapPt);
109889             if (dist < this$1._snapTolerance && dist < minDist) {
109890               minDist = dist;
109891               snapIndex = i;
109892             }
109893           }
109894           return snapIndex
109895         };
109896         LineStringSnapper.prototype.setAllowSnappingToSourceVertices = function setAllowSnappingToSourceVertices (allowSnappingToSourceVertices) {
109897           this._allowSnappingToSourceVertices = allowSnappingToSourceVertices;
109898         };
109899         LineStringSnapper.prototype.interfaces_ = function interfaces_ () {
109900           return []
109901         };
109902         LineStringSnapper.prototype.getClass = function getClass () {
109903           return LineStringSnapper
109904         };
109905         LineStringSnapper.isClosed = function isClosed (pts) {
109906           if (pts.length <= 1) { return false }
109907           return pts[0].equals2D(pts[pts.length - 1])
109908         };
109909
109910         var GeometrySnapper = function GeometrySnapper (srcGeom) {
109911           this._srcGeom = srcGeom || null;
109912         };
109913
109914         var staticAccessors$41 = { SNAP_PRECISION_FACTOR: { configurable: true } };
109915         GeometrySnapper.prototype.snapTo = function snapTo (snapGeom, snapTolerance) {
109916           var snapPts = this.extractTargetCoordinates(snapGeom);
109917           var snapTrans = new SnapTransformer(snapTolerance, snapPts);
109918           return snapTrans.transform(this._srcGeom)
109919         };
109920         GeometrySnapper.prototype.snapToSelf = function snapToSelf (snapTolerance, cleanResult) {
109921           var snapPts = this.extractTargetCoordinates(this._srcGeom);
109922           var snapTrans = new SnapTransformer(snapTolerance, snapPts, true);
109923           var snappedGeom = snapTrans.transform(this._srcGeom);
109924           var result = snappedGeom;
109925           if (cleanResult && hasInterface(result, Polygonal)) {
109926             result = snappedGeom.buffer(0);
109927           }
109928           return result
109929         };
109930         GeometrySnapper.prototype.computeSnapTolerance = function computeSnapTolerance (ringPts) {
109931           var minSegLen = this.computeMinimumSegmentLength(ringPts);
109932           var snapTol = minSegLen / 10;
109933           return snapTol
109934         };
109935         GeometrySnapper.prototype.extractTargetCoordinates = function extractTargetCoordinates (g) {
109936           var ptSet = new TreeSet();
109937           var pts = g.getCoordinates();
109938           for (var i = 0; i < pts.length; i++) {
109939             ptSet.add(pts[i]);
109940           }
109941           return ptSet.toArray(new Array(0).fill(null))
109942         };
109943         GeometrySnapper.prototype.computeMinimumSegmentLength = function computeMinimumSegmentLength (pts) {
109944           var minSegLen = Double.MAX_VALUE;
109945           for (var i = 0; i < pts.length - 1; i++) {
109946             var segLen = pts[i].distance(pts[i + 1]);
109947             if (segLen < minSegLen) { minSegLen = segLen; }
109948           }
109949           return minSegLen
109950         };
109951         GeometrySnapper.prototype.interfaces_ = function interfaces_ () {
109952           return []
109953         };
109954         GeometrySnapper.prototype.getClass = function getClass () {
109955           return GeometrySnapper
109956         };
109957         GeometrySnapper.snap = function snap (g0, g1, snapTolerance) {
109958           var snapGeom = new Array(2).fill(null);
109959           var snapper0 = new GeometrySnapper(g0);
109960           snapGeom[0] = snapper0.snapTo(g1, snapTolerance);
109961           var snapper1 = new GeometrySnapper(g1);
109962           snapGeom[1] = snapper1.snapTo(snapGeom[0], snapTolerance);
109963           return snapGeom
109964         };
109965         GeometrySnapper.computeOverlaySnapTolerance = function computeOverlaySnapTolerance () {
109966           if (arguments.length === 1) {
109967             var g = arguments[0];
109968             var snapTolerance = GeometrySnapper.computeSizeBasedSnapTolerance(g);
109969             var pm = g.getPrecisionModel();
109970             if (pm.getType() === PrecisionModel.FIXED) {
109971               var fixedSnapTol = 1 / pm.getScale() * 2 / 1.415;
109972               if (fixedSnapTol > snapTolerance) { snapTolerance = fixedSnapTol; }
109973             }
109974             return snapTolerance
109975           } else if (arguments.length === 2) {
109976             var g0 = arguments[0];
109977             var g1 = arguments[1];
109978             return Math.min(GeometrySnapper.computeOverlaySnapTolerance(g0), GeometrySnapper.computeOverlaySnapTolerance(g1))
109979           }
109980         };
109981         GeometrySnapper.computeSizeBasedSnapTolerance = function computeSizeBasedSnapTolerance (g) {
109982           var env = g.getEnvelopeInternal();
109983           var minDimension = Math.min(env.getHeight(), env.getWidth());
109984           var snapTol = minDimension * GeometrySnapper.SNAP_PRECISION_FACTOR;
109985           return snapTol
109986         };
109987         GeometrySnapper.snapToSelf = function snapToSelf (geom, snapTolerance, cleanResult) {
109988           var snapper0 = new GeometrySnapper(geom);
109989           return snapper0.snapToSelf(snapTolerance, cleanResult)
109990         };
109991         staticAccessors$41.SNAP_PRECISION_FACTOR.get = function () { return 1e-9 };
109992
109993         Object.defineProperties( GeometrySnapper, staticAccessors$41 );
109994
109995         var SnapTransformer = (function (GeometryTransformer$$1) {
109996           function SnapTransformer (snapTolerance, snapPts, isSelfSnap) {
109997             GeometryTransformer$$1.call(this);
109998             this._snapTolerance = snapTolerance || null;
109999             this._snapPts = snapPts || null;
110000             this._isSelfSnap = (isSelfSnap !== undefined) ? isSelfSnap : false;
110001           }
110002
110003           if ( GeometryTransformer$$1 ) { SnapTransformer.__proto__ = GeometryTransformer$$1; }
110004           SnapTransformer.prototype = Object.create( GeometryTransformer$$1 && GeometryTransformer$$1.prototype );
110005           SnapTransformer.prototype.constructor = SnapTransformer;
110006           SnapTransformer.prototype.snapLine = function snapLine (srcPts, snapPts) {
110007             var snapper = new LineStringSnapper(srcPts, this._snapTolerance);
110008             snapper.setAllowSnappingToSourceVertices(this._isSelfSnap);
110009             return snapper.snapTo(snapPts)
110010           };
110011           SnapTransformer.prototype.transformCoordinates = function transformCoordinates (coords, parent) {
110012             var srcPts = coords.toCoordinateArray();
110013             var newPts = this.snapLine(srcPts, this._snapPts);
110014             return this._factory.getCoordinateSequenceFactory().create(newPts)
110015           };
110016           SnapTransformer.prototype.interfaces_ = function interfaces_ () {
110017             return []
110018           };
110019           SnapTransformer.prototype.getClass = function getClass () {
110020             return SnapTransformer
110021           };
110022
110023           return SnapTransformer;
110024         }(GeometryTransformer));
110025
110026         var CommonBits = function CommonBits () {
110027           this._isFirst = true;
110028           this._commonMantissaBitsCount = 53;
110029           this._commonBits = 0;
110030           this._commonSignExp = null;
110031         };
110032         CommonBits.prototype.getCommon = function getCommon () {
110033           return Double.longBitsToDouble(this._commonBits)
110034         };
110035         CommonBits.prototype.add = function add (num) {
110036           var numBits = Double.doubleToLongBits(num);
110037           if (this._isFirst) {
110038             this._commonBits = numBits;
110039             this._commonSignExp = CommonBits.signExpBits(this._commonBits);
110040             this._isFirst = false;
110041             return null
110042           }
110043           var numSignExp = CommonBits.signExpBits(numBits);
110044           if (numSignExp !== this._commonSignExp) {
110045             this._commonBits = 0;
110046             return null
110047           }
110048           this._commonMantissaBitsCount = CommonBits.numCommonMostSigMantissaBits(this._commonBits, numBits);
110049           this._commonBits = CommonBits.zeroLowerBits(this._commonBits, 64 - (12 + this._commonMantissaBitsCount));
110050         };
110051         CommonBits.prototype.toString = function toString () {
110052           if (arguments.length === 1) {
110053             var bits = arguments[0];
110054             var x = Double.longBitsToDouble(bits);
110055             var numStr = Double.toBinaryString(bits);
110056             var padStr = '0000000000000000000000000000000000000000000000000000000000000000' + numStr;
110057             var bitStr = padStr.substring(padStr.length - 64);
110058             var str = bitStr.substring(0, 1) + '  ' + bitStr.substring(1, 12) + '(exp) ' + bitStr.substring(12) + ' [ ' + x + ' ]';
110059             return str
110060           }
110061         };
110062         CommonBits.prototype.interfaces_ = function interfaces_ () {
110063           return []
110064         };
110065         CommonBits.prototype.getClass = function getClass () {
110066           return CommonBits
110067         };
110068         CommonBits.getBit = function getBit (bits, i) {
110069           var mask = 1 << i;
110070           return (bits & mask) !== 0 ? 1 : 0
110071         };
110072         CommonBits.signExpBits = function signExpBits (num) {
110073           return num >> 52
110074         };
110075         CommonBits.zeroLowerBits = function zeroLowerBits (bits, nBits) {
110076           var invMask = (1 << nBits) - 1;
110077           var mask = ~invMask;
110078           var zeroed = bits & mask;
110079           return zeroed
110080         };
110081         CommonBits.numCommonMostSigMantissaBits = function numCommonMostSigMantissaBits (num1, num2) {
110082           var count = 0;
110083           for (var i = 52; i >= 0; i--) {
110084             if (CommonBits.getBit(num1, i) !== CommonBits.getBit(num2, i)) { return count }
110085             count++;
110086           }
110087           return 52
110088         };
110089
110090         var CommonBitsRemover = function CommonBitsRemover () {
110091           this._commonCoord = null;
110092           this._ccFilter = new CommonCoordinateFilter();
110093         };
110094
110095         var staticAccessors$42 = { CommonCoordinateFilter: { configurable: true },Translater: { configurable: true } };
110096         CommonBitsRemover.prototype.addCommonBits = function addCommonBits (geom) {
110097           var trans = new Translater(this._commonCoord);
110098           geom.apply(trans);
110099           geom.geometryChanged();
110100         };
110101         CommonBitsRemover.prototype.removeCommonBits = function removeCommonBits (geom) {
110102           if (this._commonCoord.x === 0.0 && this._commonCoord.y === 0.0) { return geom }
110103           var invCoord = new Coordinate(this._commonCoord);
110104           invCoord.x = -invCoord.x;
110105           invCoord.y = -invCoord.y;
110106           var trans = new Translater(invCoord);
110107           geom.apply(trans);
110108           geom.geometryChanged();
110109           return geom
110110         };
110111         CommonBitsRemover.prototype.getCommonCoordinate = function getCommonCoordinate () {
110112           return this._commonCoord
110113         };
110114         CommonBitsRemover.prototype.add = function add (geom) {
110115           geom.apply(this._ccFilter);
110116           this._commonCoord = this._ccFilter.getCommonCoordinate();
110117         };
110118         CommonBitsRemover.prototype.interfaces_ = function interfaces_ () {
110119           return []
110120         };
110121         CommonBitsRemover.prototype.getClass = function getClass () {
110122           return CommonBitsRemover
110123         };
110124         staticAccessors$42.CommonCoordinateFilter.get = function () { return CommonCoordinateFilter };
110125         staticAccessors$42.Translater.get = function () { return Translater };
110126
110127         Object.defineProperties( CommonBitsRemover, staticAccessors$42 );
110128
110129         var CommonCoordinateFilter = function CommonCoordinateFilter () {
110130           this._commonBitsX = new CommonBits();
110131           this._commonBitsY = new CommonBits();
110132         };
110133         CommonCoordinateFilter.prototype.filter = function filter (coord) {
110134           this._commonBitsX.add(coord.x);
110135           this._commonBitsY.add(coord.y);
110136         };
110137         CommonCoordinateFilter.prototype.getCommonCoordinate = function getCommonCoordinate () {
110138           return new Coordinate(this._commonBitsX.getCommon(), this._commonBitsY.getCommon())
110139         };
110140         CommonCoordinateFilter.prototype.interfaces_ = function interfaces_ () {
110141           return [CoordinateFilter]
110142         };
110143         CommonCoordinateFilter.prototype.getClass = function getClass () {
110144           return CommonCoordinateFilter
110145         };
110146
110147         var Translater = function Translater () {
110148           this.trans = null;
110149           var trans = arguments[0];
110150           this.trans = trans;
110151         };
110152         Translater.prototype.filter = function filter (seq, i) {
110153           var xp = seq.getOrdinate(i, 0) + this.trans.x;
110154           var yp = seq.getOrdinate(i, 1) + this.trans.y;
110155           seq.setOrdinate(i, 0, xp);
110156           seq.setOrdinate(i, 1, yp);
110157         };
110158         Translater.prototype.isDone = function isDone () {
110159           return false
110160         };
110161         Translater.prototype.isGeometryChanged = function isGeometryChanged () {
110162           return true
110163         };
110164         Translater.prototype.interfaces_ = function interfaces_ () {
110165           return [CoordinateSequenceFilter]
110166         };
110167         Translater.prototype.getClass = function getClass () {
110168           return Translater
110169         };
110170
110171         var SnapOverlayOp = function SnapOverlayOp (g1, g2) {
110172           this._geom = new Array(2).fill(null);
110173           this._snapTolerance = null;
110174           this._cbr = null;
110175           this._geom[0] = g1;
110176           this._geom[1] = g2;
110177           this.computeSnapTolerance();
110178         };
110179         SnapOverlayOp.prototype.selfSnap = function selfSnap (geom) {
110180           var snapper0 = new GeometrySnapper(geom);
110181           var snapGeom = snapper0.snapTo(geom, this._snapTolerance);
110182           return snapGeom
110183         };
110184         SnapOverlayOp.prototype.removeCommonBits = function removeCommonBits (geom) {
110185           this._cbr = new CommonBitsRemover();
110186           this._cbr.add(geom[0]);
110187           this._cbr.add(geom[1]);
110188           var remGeom = new Array(2).fill(null);
110189           remGeom[0] = this._cbr.removeCommonBits(geom[0].copy());
110190           remGeom[1] = this._cbr.removeCommonBits(geom[1].copy());
110191           return remGeom
110192         };
110193         SnapOverlayOp.prototype.prepareResult = function prepareResult (geom) {
110194           this._cbr.addCommonBits(geom);
110195           return geom
110196         };
110197         SnapOverlayOp.prototype.getResultGeometry = function getResultGeometry (opCode) {
110198           var prepGeom = this.snap(this._geom);
110199           var result = OverlayOp.overlayOp(prepGeom[0], prepGeom[1], opCode);
110200           return this.prepareResult(result)
110201         };
110202         SnapOverlayOp.prototype.checkValid = function checkValid (g) {
110203           if (!g.isValid()) {
110204             System.out.println('Snapped geometry is invalid');
110205           }
110206         };
110207         SnapOverlayOp.prototype.computeSnapTolerance = function computeSnapTolerance () {
110208           this._snapTolerance = GeometrySnapper.computeOverlaySnapTolerance(this._geom[0], this._geom[1]);
110209         };
110210         SnapOverlayOp.prototype.snap = function snap (geom) {
110211           var remGeom = this.removeCommonBits(geom);
110212           var snapGeom = GeometrySnapper.snap(remGeom[0], remGeom[1], this._snapTolerance);
110213           return snapGeom
110214         };
110215         SnapOverlayOp.prototype.interfaces_ = function interfaces_ () {
110216           return []
110217         };
110218         SnapOverlayOp.prototype.getClass = function getClass () {
110219           return SnapOverlayOp
110220         };
110221         SnapOverlayOp.overlayOp = function overlayOp (g0, g1, opCode) {
110222           var op = new SnapOverlayOp(g0, g1);
110223           return op.getResultGeometry(opCode)
110224         };
110225         SnapOverlayOp.union = function union (g0, g1) {
110226           return SnapOverlayOp.overlayOp(g0, g1, OverlayOp.UNION)
110227         };
110228         SnapOverlayOp.intersection = function intersection (g0, g1) {
110229           return SnapOverlayOp.overlayOp(g0, g1, OverlayOp.INTERSECTION)
110230         };
110231         SnapOverlayOp.symDifference = function symDifference (g0, g1) {
110232           return SnapOverlayOp.overlayOp(g0, g1, OverlayOp.SYMDIFFERENCE)
110233         };
110234         SnapOverlayOp.difference = function difference (g0, g1) {
110235           return SnapOverlayOp.overlayOp(g0, g1, OverlayOp.DIFFERENCE)
110236         };
110237
110238         var SnapIfNeededOverlayOp = function SnapIfNeededOverlayOp (g1, g2) {
110239           this._geom = new Array(2).fill(null);
110240           this._geom[0] = g1;
110241           this._geom[1] = g2;
110242         };
110243         SnapIfNeededOverlayOp.prototype.getResultGeometry = function getResultGeometry (opCode) {
110244           var result = null;
110245           var isSuccess = false;
110246           var savedException = null;
110247           try {
110248             result = OverlayOp.overlayOp(this._geom[0], this._geom[1], opCode);
110249             var isValid = true;
110250             if (isValid) { isSuccess = true; }
110251           } catch (ex) {
110252             if (ex instanceof RuntimeException) {
110253               savedException = ex;
110254             } else { throw ex }
110255           } finally {}
110256           if (!isSuccess) {
110257             try {
110258               result = SnapOverlayOp.overlayOp(this._geom[0], this._geom[1], opCode);
110259             } catch (ex$1) {
110260               if (ex$1 instanceof RuntimeException) {
110261                 throw savedException
110262               } else { throw ex$1 }
110263             } finally {}
110264           }
110265           return result
110266         };
110267         SnapIfNeededOverlayOp.prototype.interfaces_ = function interfaces_ () {
110268           return []
110269         };
110270         SnapIfNeededOverlayOp.prototype.getClass = function getClass () {
110271           return SnapIfNeededOverlayOp
110272         };
110273         SnapIfNeededOverlayOp.overlayOp = function overlayOp (g0, g1, opCode) {
110274           var op = new SnapIfNeededOverlayOp(g0, g1);
110275           return op.getResultGeometry(opCode)
110276         };
110277         SnapIfNeededOverlayOp.union = function union (g0, g1) {
110278           return SnapIfNeededOverlayOp.overlayOp(g0, g1, OverlayOp.UNION)
110279         };
110280         SnapIfNeededOverlayOp.intersection = function intersection (g0, g1) {
110281           return SnapIfNeededOverlayOp.overlayOp(g0, g1, OverlayOp.INTERSECTION)
110282         };
110283         SnapIfNeededOverlayOp.symDifference = function symDifference (g0, g1) {
110284           return SnapIfNeededOverlayOp.overlayOp(g0, g1, OverlayOp.SYMDIFFERENCE)
110285         };
110286         SnapIfNeededOverlayOp.difference = function difference (g0, g1) {
110287           return SnapIfNeededOverlayOp.overlayOp(g0, g1, OverlayOp.DIFFERENCE)
110288         };
110289
110290         var MonotoneChain$2 = function MonotoneChain () {
110291           this.mce = null;
110292           this.chainIndex = null;
110293           var mce = arguments[0];
110294           var chainIndex = arguments[1];
110295           this.mce = mce;
110296           this.chainIndex = chainIndex;
110297         };
110298         MonotoneChain$2.prototype.computeIntersections = function computeIntersections (mc, si) {
110299           this.mce.computeIntersectsForChain(this.chainIndex, mc.mce, mc.chainIndex, si);
110300         };
110301         MonotoneChain$2.prototype.interfaces_ = function interfaces_ () {
110302           return []
110303         };
110304         MonotoneChain$2.prototype.getClass = function getClass () {
110305           return MonotoneChain$2
110306         };
110307
110308         var SweepLineEvent = function SweepLineEvent () {
110309           this._label = null;
110310           this._xValue = null;
110311           this._eventType = null;
110312           this._insertEvent = null;
110313           this._deleteEventIndex = null;
110314           this._obj = null;
110315           if (arguments.length === 2) {
110316             var x = arguments[0];
110317             var insertEvent = arguments[1];
110318             this._eventType = SweepLineEvent.DELETE;
110319             this._xValue = x;
110320             this._insertEvent = insertEvent;
110321           } else if (arguments.length === 3) {
110322             var label = arguments[0];
110323             var x$1 = arguments[1];
110324             var obj = arguments[2];
110325             this._eventType = SweepLineEvent.INSERT;
110326             this._label = label;
110327             this._xValue = x$1;
110328             this._obj = obj;
110329           }
110330         };
110331
110332         var staticAccessors$43 = { INSERT: { configurable: true },DELETE: { configurable: true } };
110333         SweepLineEvent.prototype.isDelete = function isDelete () {
110334           return this._eventType === SweepLineEvent.DELETE
110335         };
110336         SweepLineEvent.prototype.setDeleteEventIndex = function setDeleteEventIndex (deleteEventIndex) {
110337           this._deleteEventIndex = deleteEventIndex;
110338         };
110339         SweepLineEvent.prototype.getObject = function getObject () {
110340           return this._obj
110341         };
110342         SweepLineEvent.prototype.compareTo = function compareTo (o) {
110343           var pe = o;
110344           if (this._xValue < pe._xValue) { return -1 }
110345           if (this._xValue > pe._xValue) { return 1 }
110346           if (this._eventType < pe._eventType) { return -1 }
110347           if (this._eventType > pe._eventType) { return 1 }
110348           return 0
110349         };
110350         SweepLineEvent.prototype.getInsertEvent = function getInsertEvent () {
110351           return this._insertEvent
110352         };
110353         SweepLineEvent.prototype.isInsert = function isInsert () {
110354           return this._eventType === SweepLineEvent.INSERT
110355         };
110356         SweepLineEvent.prototype.isSameLabel = function isSameLabel (ev) {
110357           if (this._label === null) { return false }
110358           return this._label === ev._label
110359         };
110360         SweepLineEvent.prototype.getDeleteEventIndex = function getDeleteEventIndex () {
110361           return this._deleteEventIndex
110362         };
110363         SweepLineEvent.prototype.interfaces_ = function interfaces_ () {
110364           return [Comparable]
110365         };
110366         SweepLineEvent.prototype.getClass = function getClass () {
110367           return SweepLineEvent
110368         };
110369         staticAccessors$43.INSERT.get = function () { return 1 };
110370         staticAccessors$43.DELETE.get = function () { return 2 };
110371
110372         Object.defineProperties( SweepLineEvent, staticAccessors$43 );
110373
110374         var EdgeSetIntersector = function EdgeSetIntersector () {};
110375
110376         EdgeSetIntersector.prototype.interfaces_ = function interfaces_ () {
110377           return []
110378         };
110379         EdgeSetIntersector.prototype.getClass = function getClass () {
110380           return EdgeSetIntersector
110381         };
110382
110383         var SegmentIntersector$2 = function SegmentIntersector () {
110384           this._hasIntersection = false;
110385           this._hasProper = false;
110386           this._hasProperInterior = false;
110387           this._properIntersectionPoint = null;
110388           this._li = null;
110389           this._includeProper = null;
110390           this._recordIsolated = null;
110391           this._isSelfIntersection = null;
110392           this._numIntersections = 0;
110393           this.numTests = 0;
110394           this._bdyNodes = null;
110395           this._isDone = false;
110396           this._isDoneWhenProperInt = false;
110397           var li = arguments[0];
110398           var includeProper = arguments[1];
110399           var recordIsolated = arguments[2];
110400           this._li = li;
110401           this._includeProper = includeProper;
110402           this._recordIsolated = recordIsolated;
110403         };
110404         SegmentIntersector$2.prototype.isTrivialIntersection = function isTrivialIntersection (e0, segIndex0, e1, segIndex1) {
110405           if (e0 === e1) {
110406             if (this._li.getIntersectionNum() === 1) {
110407               if (SegmentIntersector$2.isAdjacentSegments(segIndex0, segIndex1)) { return true }
110408               if (e0.isClosed()) {
110409                 var maxSegIndex = e0.getNumPoints() - 1;
110410                 if ((segIndex0 === 0 && segIndex1 === maxSegIndex) ||
110411                     (segIndex1 === 0 && segIndex0 === maxSegIndex)) {
110412                   return true
110413                 }
110414               }
110415             }
110416           }
110417           return false
110418         };
110419         SegmentIntersector$2.prototype.getProperIntersectionPoint = function getProperIntersectionPoint () {
110420           return this._properIntersectionPoint
110421         };
110422         SegmentIntersector$2.prototype.setIsDoneIfProperInt = function setIsDoneIfProperInt (isDoneWhenProperInt) {
110423           this._isDoneWhenProperInt = isDoneWhenProperInt;
110424         };
110425         SegmentIntersector$2.prototype.hasProperInteriorIntersection = function hasProperInteriorIntersection () {
110426           return this._hasProperInterior
110427         };
110428         SegmentIntersector$2.prototype.isBoundaryPointInternal = function isBoundaryPointInternal (li, bdyNodes) {
110429           for (var i = bdyNodes.iterator(); i.hasNext();) {
110430             var node = i.next();
110431             var pt = node.getCoordinate();
110432             if (li.isIntersection(pt)) { return true }
110433           }
110434           return false
110435         };
110436         SegmentIntersector$2.prototype.hasProperIntersection = function hasProperIntersection () {
110437           return this._hasProper
110438         };
110439         SegmentIntersector$2.prototype.hasIntersection = function hasIntersection () {
110440           return this._hasIntersection
110441         };
110442         SegmentIntersector$2.prototype.isDone = function isDone () {
110443           return this._isDone
110444         };
110445         SegmentIntersector$2.prototype.isBoundaryPoint = function isBoundaryPoint (li, bdyNodes) {
110446           if (bdyNodes === null) { return false }
110447           if (this.isBoundaryPointInternal(li, bdyNodes[0])) { return true }
110448           if (this.isBoundaryPointInternal(li, bdyNodes[1])) { return true }
110449           return false
110450         };
110451         SegmentIntersector$2.prototype.setBoundaryNodes = function setBoundaryNodes (bdyNodes0, bdyNodes1) {
110452           this._bdyNodes = new Array(2).fill(null);
110453           this._bdyNodes[0] = bdyNodes0;
110454           this._bdyNodes[1] = bdyNodes1;
110455         };
110456         SegmentIntersector$2.prototype.addIntersections = function addIntersections (e0, segIndex0, e1, segIndex1) {
110457           if (e0 === e1 && segIndex0 === segIndex1) { return null }
110458           this.numTests++;
110459           var p00 = e0.getCoordinates()[segIndex0];
110460           var p01 = e0.getCoordinates()[segIndex0 + 1];
110461           var p10 = e1.getCoordinates()[segIndex1];
110462           var p11 = e1.getCoordinates()[segIndex1 + 1];
110463           this._li.computeIntersection(p00, p01, p10, p11);
110464           if (this._li.hasIntersection()) {
110465             if (this._recordIsolated) {
110466               e0.setIsolated(false);
110467               e1.setIsolated(false);
110468             }
110469             this._numIntersections++;
110470             if (!this.isTrivialIntersection(e0, segIndex0, e1, segIndex1)) {
110471               this._hasIntersection = true;
110472               if (this._includeProper || !this._li.isProper()) {
110473                 e0.addIntersections(this._li, segIndex0, 0);
110474                 e1.addIntersections(this._li, segIndex1, 1);
110475               }
110476               if (this._li.isProper()) {
110477                 this._properIntersectionPoint = this._li.getIntersection(0).copy();
110478                 this._hasProper = true;
110479                 if (this._isDoneWhenProperInt) {
110480                   this._isDone = true;
110481                 }
110482                 if (!this.isBoundaryPoint(this._li, this._bdyNodes)) { this._hasProperInterior = true; }
110483               }
110484             }
110485           }
110486         };
110487         SegmentIntersector$2.prototype.interfaces_ = function interfaces_ () {
110488           return []
110489         };
110490         SegmentIntersector$2.prototype.getClass = function getClass () {
110491           return SegmentIntersector$2
110492         };
110493         SegmentIntersector$2.isAdjacentSegments = function isAdjacentSegments (i1, i2) {
110494           return Math.abs(i1 - i2) === 1
110495         };
110496
110497         var SimpleMCSweepLineIntersector = (function (EdgeSetIntersector$$1) {
110498           function SimpleMCSweepLineIntersector () {
110499             EdgeSetIntersector$$1.call(this);
110500             this.events = new ArrayList();
110501             this.nOverlaps = null;
110502           }
110503
110504           if ( EdgeSetIntersector$$1 ) { SimpleMCSweepLineIntersector.__proto__ = EdgeSetIntersector$$1; }
110505           SimpleMCSweepLineIntersector.prototype = Object.create( EdgeSetIntersector$$1 && EdgeSetIntersector$$1.prototype );
110506           SimpleMCSweepLineIntersector.prototype.constructor = SimpleMCSweepLineIntersector;
110507           SimpleMCSweepLineIntersector.prototype.prepareEvents = function prepareEvents () {
110508             var this$1 = this;
110509
110510             Collections.sort(this.events);
110511             for (var i = 0; i < this.events.size(); i++) {
110512               var ev = this$1.events.get(i);
110513               if (ev.isDelete()) {
110514                 ev.getInsertEvent().setDeleteEventIndex(i);
110515               }
110516             }
110517           };
110518           SimpleMCSweepLineIntersector.prototype.computeIntersections = function computeIntersections () {
110519             var this$1 = this;
110520
110521             if (arguments.length === 1) {
110522               var si = arguments[0];
110523               this.nOverlaps = 0;
110524               this.prepareEvents();
110525               for (var i = 0; i < this.events.size(); i++) {
110526                 var ev = this$1.events.get(i);
110527                 if (ev.isInsert()) {
110528                   this$1.processOverlaps(i, ev.getDeleteEventIndex(), ev, si);
110529                 }
110530                 if (si.isDone()) {
110531                   break
110532                 }
110533               }
110534             } else if (arguments.length === 3) {
110535               if (arguments[2] instanceof SegmentIntersector$2 && (hasInterface(arguments[0], List) && hasInterface(arguments[1], List))) {
110536                 var edges0 = arguments[0];
110537                 var edges1 = arguments[1];
110538                 var si$1 = arguments[2];
110539                 this.addEdges(edges0, edges0);
110540                 this.addEdges(edges1, edges1);
110541                 this.computeIntersections(si$1);
110542               } else if (typeof arguments[2] === 'boolean' && (hasInterface(arguments[0], List) && arguments[1] instanceof SegmentIntersector$2)) {
110543                 var edges = arguments[0];
110544                 var si$2 = arguments[1];
110545                 var testAllSegments = arguments[2];
110546                 if (testAllSegments) { this.addEdges(edges, null); } else { this.addEdges(edges); }
110547                 this.computeIntersections(si$2);
110548               }
110549             }
110550           };
110551           SimpleMCSweepLineIntersector.prototype.addEdge = function addEdge (edge, edgeSet) {
110552             var this$1 = this;
110553
110554             var mce = edge.getMonotoneChainEdge();
110555             var startIndex = mce.getStartIndexes();
110556             for (var i = 0; i < startIndex.length - 1; i++) {
110557               var mc = new MonotoneChain$2(mce, i);
110558               var insertEvent = new SweepLineEvent(edgeSet, mce.getMinX(i), mc);
110559               this$1.events.add(insertEvent);
110560               this$1.events.add(new SweepLineEvent(mce.getMaxX(i), insertEvent));
110561             }
110562           };
110563           SimpleMCSweepLineIntersector.prototype.processOverlaps = function processOverlaps (start, end, ev0, si) {
110564             var this$1 = this;
110565
110566             var mc0 = ev0.getObject();
110567             for (var i = start; i < end; i++) {
110568               var ev1 = this$1.events.get(i);
110569               if (ev1.isInsert()) {
110570                 var mc1 = ev1.getObject();
110571                 if (!ev0.isSameLabel(ev1)) {
110572                   mc0.computeIntersections(mc1, si);
110573                   this$1.nOverlaps++;
110574                 }
110575               }
110576             }
110577           };
110578           SimpleMCSweepLineIntersector.prototype.addEdges = function addEdges () {
110579             var this$1 = this;
110580
110581             if (arguments.length === 1) {
110582               var edges = arguments[0];
110583               for (var i = edges.iterator(); i.hasNext();) {
110584                 var edge = i.next();
110585                 this$1.addEdge(edge, edge);
110586               }
110587             } else if (arguments.length === 2) {
110588               var edges$1 = arguments[0];
110589               var edgeSet = arguments[1];
110590               for (var i$1 = edges$1.iterator(); i$1.hasNext();) {
110591                 var edge$1 = i$1.next();
110592                 this$1.addEdge(edge$1, edgeSet);
110593               }
110594             }
110595           };
110596           SimpleMCSweepLineIntersector.prototype.interfaces_ = function interfaces_ () {
110597             return []
110598           };
110599           SimpleMCSweepLineIntersector.prototype.getClass = function getClass () {
110600             return SimpleMCSweepLineIntersector
110601           };
110602
110603           return SimpleMCSweepLineIntersector;
110604         }(EdgeSetIntersector));
110605
110606         var IntervalRTreeNode = function IntervalRTreeNode () {
110607           this._min = Double.POSITIVE_INFINITY;
110608           this._max = Double.NEGATIVE_INFINITY;
110609         };
110610
110611         var staticAccessors$45 = { NodeComparator: { configurable: true } };
110612         IntervalRTreeNode.prototype.getMin = function getMin () {
110613           return this._min
110614         };
110615         IntervalRTreeNode.prototype.intersects = function intersects (queryMin, queryMax) {
110616           if (this._min > queryMax || this._max < queryMin) { return false }
110617           return true
110618         };
110619         IntervalRTreeNode.prototype.getMax = function getMax () {
110620           return this._max
110621         };
110622         IntervalRTreeNode.prototype.toString = function toString () {
110623           return WKTWriter.toLineString(new Coordinate(this._min, 0), new Coordinate(this._max, 0))
110624         };
110625         IntervalRTreeNode.prototype.interfaces_ = function interfaces_ () {
110626           return []
110627         };
110628         IntervalRTreeNode.prototype.getClass = function getClass () {
110629           return IntervalRTreeNode
110630         };
110631         staticAccessors$45.NodeComparator.get = function () { return NodeComparator };
110632
110633         Object.defineProperties( IntervalRTreeNode, staticAccessors$45 );
110634
110635         var NodeComparator = function NodeComparator () {};
110636
110637         NodeComparator.prototype.compare = function compare (o1, o2) {
110638           var n1 = o1;
110639           var n2 = o2;
110640           var mid1 = (n1._min + n1._max) / 2;
110641           var mid2 = (n2._min + n2._max) / 2;
110642           if (mid1 < mid2) { return -1 }
110643           if (mid1 > mid2) { return 1 }
110644           return 0
110645         };
110646         NodeComparator.prototype.interfaces_ = function interfaces_ () {
110647           return [Comparator]
110648         };
110649         NodeComparator.prototype.getClass = function getClass () {
110650           return NodeComparator
110651         };
110652
110653         var IntervalRTreeLeafNode = (function (IntervalRTreeNode$$1) {
110654           function IntervalRTreeLeafNode () {
110655             IntervalRTreeNode$$1.call(this);
110656             this._item = null;
110657             var min = arguments[0];
110658             var max = arguments[1];
110659             var item = arguments[2];
110660             this._min = min;
110661             this._max = max;
110662             this._item = item;
110663           }
110664
110665           if ( IntervalRTreeNode$$1 ) { IntervalRTreeLeafNode.__proto__ = IntervalRTreeNode$$1; }
110666           IntervalRTreeLeafNode.prototype = Object.create( IntervalRTreeNode$$1 && IntervalRTreeNode$$1.prototype );
110667           IntervalRTreeLeafNode.prototype.constructor = IntervalRTreeLeafNode;
110668           IntervalRTreeLeafNode.prototype.query = function query (queryMin, queryMax, visitor) {
110669             if (!this.intersects(queryMin, queryMax)) { return null }
110670             visitor.visitItem(this._item);
110671           };
110672           IntervalRTreeLeafNode.prototype.interfaces_ = function interfaces_ () {
110673             return []
110674           };
110675           IntervalRTreeLeafNode.prototype.getClass = function getClass () {
110676             return IntervalRTreeLeafNode
110677           };
110678
110679           return IntervalRTreeLeafNode;
110680         }(IntervalRTreeNode));
110681
110682         var IntervalRTreeBranchNode = (function (IntervalRTreeNode$$1) {
110683           function IntervalRTreeBranchNode () {
110684             IntervalRTreeNode$$1.call(this);
110685             this._node1 = null;
110686             this._node2 = null;
110687             var n1 = arguments[0];
110688             var n2 = arguments[1];
110689             this._node1 = n1;
110690             this._node2 = n2;
110691             this.buildExtent(this._node1, this._node2);
110692           }
110693
110694           if ( IntervalRTreeNode$$1 ) { IntervalRTreeBranchNode.__proto__ = IntervalRTreeNode$$1; }
110695           IntervalRTreeBranchNode.prototype = Object.create( IntervalRTreeNode$$1 && IntervalRTreeNode$$1.prototype );
110696           IntervalRTreeBranchNode.prototype.constructor = IntervalRTreeBranchNode;
110697           IntervalRTreeBranchNode.prototype.buildExtent = function buildExtent (n1, n2) {
110698             this._min = Math.min(n1._min, n2._min);
110699             this._max = Math.max(n1._max, n2._max);
110700           };
110701           IntervalRTreeBranchNode.prototype.query = function query (queryMin, queryMax, visitor) {
110702             if (!this.intersects(queryMin, queryMax)) {
110703               return null
110704             }
110705             if (this._node1 !== null) { this._node1.query(queryMin, queryMax, visitor); }
110706             if (this._node2 !== null) { this._node2.query(queryMin, queryMax, visitor); }
110707           };
110708           IntervalRTreeBranchNode.prototype.interfaces_ = function interfaces_ () {
110709             return []
110710           };
110711           IntervalRTreeBranchNode.prototype.getClass = function getClass () {
110712             return IntervalRTreeBranchNode
110713           };
110714
110715           return IntervalRTreeBranchNode;
110716         }(IntervalRTreeNode));
110717
110718         var SortedPackedIntervalRTree = function SortedPackedIntervalRTree () {
110719           this._leaves = new ArrayList();
110720           this._root = null;
110721           this._level = 0;
110722         };
110723         SortedPackedIntervalRTree.prototype.buildTree = function buildTree () {
110724             var this$1 = this;
110725
110726           Collections.sort(this._leaves, new IntervalRTreeNode.NodeComparator());
110727           var src = this._leaves;
110728           var temp = null;
110729           var dest = new ArrayList();
110730           while (true) {
110731             this$1.buildLevel(src, dest);
110732             if (dest.size() === 1) { return dest.get(0) }
110733             temp = src;
110734             src = dest;
110735             dest = temp;
110736           }
110737         };
110738         SortedPackedIntervalRTree.prototype.insert = function insert (min, max, item) {
110739           if (this._root !== null) { throw new Error('Index cannot be added to once it has been queried') }
110740           this._leaves.add(new IntervalRTreeLeafNode(min, max, item));
110741         };
110742         SortedPackedIntervalRTree.prototype.query = function query (min, max, visitor) {
110743           this.init();
110744           this._root.query(min, max, visitor);
110745         };
110746         SortedPackedIntervalRTree.prototype.buildRoot = function buildRoot () {
110747           if (this._root !== null) { return null }
110748           this._root = this.buildTree();
110749         };
110750         SortedPackedIntervalRTree.prototype.printNode = function printNode (node) {
110751           System.out.println(WKTWriter.toLineString(new Coordinate(node._min, this._level), new Coordinate(node._max, this._level)));
110752         };
110753         SortedPackedIntervalRTree.prototype.init = function init () {
110754           if (this._root !== null) { return null }
110755           this.buildRoot();
110756         };
110757         SortedPackedIntervalRTree.prototype.buildLevel = function buildLevel (src, dest) {
110758           this._level++;
110759           dest.clear();
110760           for (var i = 0; i < src.size(); i += 2) {
110761             var n1 = src.get(i);
110762             var n2 = i + 1 < src.size() ? src.get(i) : null;
110763             if (n2 === null) {
110764               dest.add(n1);
110765             } else {
110766               var node = new IntervalRTreeBranchNode(src.get(i), src.get(i + 1));
110767               dest.add(node);
110768             }
110769           }
110770         };
110771         SortedPackedIntervalRTree.prototype.interfaces_ = function interfaces_ () {
110772           return []
110773         };
110774         SortedPackedIntervalRTree.prototype.getClass = function getClass () {
110775           return SortedPackedIntervalRTree
110776         };
110777
110778         var ArrayListVisitor = function ArrayListVisitor () {
110779           this._items = new ArrayList();
110780         };
110781         ArrayListVisitor.prototype.visitItem = function visitItem (item) {
110782           this._items.add(item);
110783         };
110784         ArrayListVisitor.prototype.getItems = function getItems () {
110785           return this._items
110786         };
110787         ArrayListVisitor.prototype.interfaces_ = function interfaces_ () {
110788           return [ItemVisitor]
110789         };
110790         ArrayListVisitor.prototype.getClass = function getClass () {
110791           return ArrayListVisitor
110792         };
110793
110794         var IndexedPointInAreaLocator = function IndexedPointInAreaLocator () {
110795           this._index = null;
110796           var g = arguments[0];
110797           if (!hasInterface(g, Polygonal)) { throw new IllegalArgumentException('Argument must be Polygonal') }
110798           this._index = new IntervalIndexedGeometry(g);
110799         };
110800
110801         var staticAccessors$44 = { SegmentVisitor: { configurable: true },IntervalIndexedGeometry: { configurable: true } };
110802         IndexedPointInAreaLocator.prototype.locate = function locate (p) {
110803           var rcc = new RayCrossingCounter(p);
110804           var visitor = new SegmentVisitor(rcc);
110805           this._index.query(p.y, p.y, visitor);
110806           return rcc.getLocation()
110807         };
110808         IndexedPointInAreaLocator.prototype.interfaces_ = function interfaces_ () {
110809           return [PointOnGeometryLocator]
110810         };
110811         IndexedPointInAreaLocator.prototype.getClass = function getClass () {
110812           return IndexedPointInAreaLocator
110813         };
110814         staticAccessors$44.SegmentVisitor.get = function () { return SegmentVisitor };
110815         staticAccessors$44.IntervalIndexedGeometry.get = function () { return IntervalIndexedGeometry };
110816
110817         Object.defineProperties( IndexedPointInAreaLocator, staticAccessors$44 );
110818
110819         var SegmentVisitor = function SegmentVisitor () {
110820           this._counter = null;
110821           var counter = arguments[0];
110822           this._counter = counter;
110823         };
110824         SegmentVisitor.prototype.visitItem = function visitItem (item) {
110825           var seg = item;
110826           this._counter.countSegment(seg.getCoordinate(0), seg.getCoordinate(1));
110827         };
110828         SegmentVisitor.prototype.interfaces_ = function interfaces_ () {
110829           return [ItemVisitor]
110830         };
110831         SegmentVisitor.prototype.getClass = function getClass () {
110832           return SegmentVisitor
110833         };
110834
110835         var IntervalIndexedGeometry = function IntervalIndexedGeometry () {
110836           this._index = new SortedPackedIntervalRTree();
110837           var geom = arguments[0];
110838           this.init(geom);
110839         };
110840         IntervalIndexedGeometry.prototype.init = function init (geom) {
110841             var this$1 = this;
110842
110843           var lines = LinearComponentExtracter.getLines(geom);
110844           for (var i = lines.iterator(); i.hasNext();) {
110845             var line = i.next();
110846             var pts = line.getCoordinates();
110847             this$1.addLine(pts);
110848           }
110849         };
110850         IntervalIndexedGeometry.prototype.addLine = function addLine (pts) {
110851             var this$1 = this;
110852
110853           for (var i = 1; i < pts.length; i++) {
110854             var seg = new LineSegment(pts[i - 1], pts[i]);
110855             var min = Math.min(seg.p0.y, seg.p1.y);
110856             var max = Math.max(seg.p0.y, seg.p1.y);
110857             this$1._index.insert(min, max, seg);
110858           }
110859         };
110860         IntervalIndexedGeometry.prototype.query = function query () {
110861           if (arguments.length === 2) {
110862             var min = arguments[0];
110863             var max = arguments[1];
110864             var visitor = new ArrayListVisitor();
110865             this._index.query(min, max, visitor);
110866             return visitor.getItems()
110867           } else if (arguments.length === 3) {
110868             var min$1 = arguments[0];
110869             var max$1 = arguments[1];
110870             var visitor$1 = arguments[2];
110871             this._index.query(min$1, max$1, visitor$1);
110872           }
110873         };
110874         IntervalIndexedGeometry.prototype.interfaces_ = function interfaces_ () {
110875           return []
110876         };
110877         IntervalIndexedGeometry.prototype.getClass = function getClass () {
110878           return IntervalIndexedGeometry
110879         };
110880
110881         var GeometryGraph = (function (PlanarGraph$$1) {
110882           function GeometryGraph () {
110883             PlanarGraph$$1.call(this);
110884             this._parentGeom = null;
110885             this._lineEdgeMap = new HashMap();
110886             this._boundaryNodeRule = null;
110887             this._useBoundaryDeterminationRule = true;
110888             this._argIndex = null;
110889             this._boundaryNodes = null;
110890             this._hasTooFewPoints = false;
110891             this._invalidPoint = null;
110892             this._areaPtLocator = null;
110893             this._ptLocator = new PointLocator();
110894             if (arguments.length === 2) {
110895               var argIndex = arguments[0];
110896               var parentGeom = arguments[1];
110897               var boundaryNodeRule = BoundaryNodeRule.OGC_SFS_BOUNDARY_RULE;
110898               this._argIndex = argIndex;
110899               this._parentGeom = parentGeom;
110900               this._boundaryNodeRule = boundaryNodeRule;
110901               if (parentGeom !== null) {
110902                 this.add(parentGeom);
110903               }
110904             } else if (arguments.length === 3) {
110905               var argIndex$1 = arguments[0];
110906               var parentGeom$1 = arguments[1];
110907               var boundaryNodeRule$1 = arguments[2];
110908               this._argIndex = argIndex$1;
110909               this._parentGeom = parentGeom$1;
110910               this._boundaryNodeRule = boundaryNodeRule$1;
110911               if (parentGeom$1 !== null) {
110912                 this.add(parentGeom$1);
110913               }
110914             }
110915           }
110916
110917           if ( PlanarGraph$$1 ) { GeometryGraph.__proto__ = PlanarGraph$$1; }
110918           GeometryGraph.prototype = Object.create( PlanarGraph$$1 && PlanarGraph$$1.prototype );
110919           GeometryGraph.prototype.constructor = GeometryGraph;
110920           GeometryGraph.prototype.insertBoundaryPoint = function insertBoundaryPoint (argIndex, coord) {
110921             var n = this._nodes.addNode(coord);
110922             var lbl = n.getLabel();
110923             var boundaryCount = 1;
110924             var loc = Location.NONE;
110925             loc = lbl.getLocation(argIndex, Position.ON);
110926             if (loc === Location.BOUNDARY) { boundaryCount++; }
110927             var newLoc = GeometryGraph.determineBoundary(this._boundaryNodeRule, boundaryCount);
110928             lbl.setLocation(argIndex, newLoc);
110929           };
110930           GeometryGraph.prototype.computeSelfNodes = function computeSelfNodes () {
110931             if (arguments.length === 2) {
110932               var li = arguments[0];
110933               var computeRingSelfNodes = arguments[1];
110934               return this.computeSelfNodes(li, computeRingSelfNodes, false)
110935             } else if (arguments.length === 3) {
110936               var li$1 = arguments[0];
110937               var computeRingSelfNodes$1 = arguments[1];
110938               var isDoneIfProperInt = arguments[2];
110939               var si = new SegmentIntersector$2(li$1, true, false);
110940               si.setIsDoneIfProperInt(isDoneIfProperInt);
110941               var esi = this.createEdgeSetIntersector();
110942               var isRings = this._parentGeom instanceof LinearRing || this._parentGeom instanceof Polygon || this._parentGeom instanceof MultiPolygon;
110943               var computeAllSegments = computeRingSelfNodes$1 || !isRings;
110944               esi.computeIntersections(this._edges, si, computeAllSegments);
110945               this.addSelfIntersectionNodes(this._argIndex);
110946               return si
110947             }
110948           };
110949           GeometryGraph.prototype.computeSplitEdges = function computeSplitEdges (edgelist) {
110950             for (var i = this._edges.iterator(); i.hasNext();) {
110951               var e = i.next();
110952               e.eiList.addSplitEdges(edgelist);
110953             }
110954           };
110955           GeometryGraph.prototype.computeEdgeIntersections = function computeEdgeIntersections (g, li, includeProper) {
110956             var si = new SegmentIntersector$2(li, includeProper, true);
110957             si.setBoundaryNodes(this.getBoundaryNodes(), g.getBoundaryNodes());
110958             var esi = this.createEdgeSetIntersector();
110959             esi.computeIntersections(this._edges, g._edges, si);
110960             return si
110961           };
110962           GeometryGraph.prototype.getGeometry = function getGeometry () {
110963             return this._parentGeom
110964           };
110965           GeometryGraph.prototype.getBoundaryNodeRule = function getBoundaryNodeRule () {
110966             return this._boundaryNodeRule
110967           };
110968           GeometryGraph.prototype.hasTooFewPoints = function hasTooFewPoints () {
110969             return this._hasTooFewPoints
110970           };
110971           GeometryGraph.prototype.addPoint = function addPoint () {
110972             if (arguments[0] instanceof Point$1) {
110973               var p = arguments[0];
110974               var coord = p.getCoordinate();
110975               this.insertPoint(this._argIndex, coord, Location.INTERIOR);
110976             } else if (arguments[0] instanceof Coordinate) {
110977               var pt = arguments[0];
110978               this.insertPoint(this._argIndex, pt, Location.INTERIOR);
110979             }
110980           };
110981           GeometryGraph.prototype.addPolygon = function addPolygon (p) {
110982             var this$1 = this;
110983
110984             this.addPolygonRing(p.getExteriorRing(), Location.EXTERIOR, Location.INTERIOR);
110985             for (var i = 0; i < p.getNumInteriorRing(); i++) {
110986               var hole = p.getInteriorRingN(i);
110987               this$1.addPolygonRing(hole, Location.INTERIOR, Location.EXTERIOR);
110988             }
110989           };
110990           GeometryGraph.prototype.addEdge = function addEdge (e) {
110991             this.insertEdge(e);
110992             var coord = e.getCoordinates();
110993             this.insertPoint(this._argIndex, coord[0], Location.BOUNDARY);
110994             this.insertPoint(this._argIndex, coord[coord.length - 1], Location.BOUNDARY);
110995           };
110996           GeometryGraph.prototype.addLineString = function addLineString (line) {
110997             var coord = CoordinateArrays.removeRepeatedPoints(line.getCoordinates());
110998             if (coord.length < 2) {
110999               this._hasTooFewPoints = true;
111000               this._invalidPoint = coord[0];
111001               return null
111002             }
111003             var e = new Edge(coord, new Label(this._argIndex, Location.INTERIOR));
111004             this._lineEdgeMap.put(line, e);
111005             this.insertEdge(e);
111006             Assert.isTrue(coord.length >= 2, 'found LineString with single point');
111007             this.insertBoundaryPoint(this._argIndex, coord[0]);
111008             this.insertBoundaryPoint(this._argIndex, coord[coord.length - 1]);
111009           };
111010           GeometryGraph.prototype.getInvalidPoint = function getInvalidPoint () {
111011             return this._invalidPoint
111012           };
111013           GeometryGraph.prototype.getBoundaryPoints = function getBoundaryPoints () {
111014             var coll = this.getBoundaryNodes();
111015             var pts = new Array(coll.size()).fill(null);
111016             var i = 0;
111017             for (var it = coll.iterator(); it.hasNext();) {
111018               var node = it.next();
111019               pts[i++] = node.getCoordinate().copy();
111020             }
111021             return pts
111022           };
111023           GeometryGraph.prototype.getBoundaryNodes = function getBoundaryNodes () {
111024             if (this._boundaryNodes === null) { this._boundaryNodes = this._nodes.getBoundaryNodes(this._argIndex); }
111025             return this._boundaryNodes
111026           };
111027           GeometryGraph.prototype.addSelfIntersectionNode = function addSelfIntersectionNode (argIndex, coord, loc) {
111028             if (this.isBoundaryNode(argIndex, coord)) { return null }
111029             if (loc === Location.BOUNDARY && this._useBoundaryDeterminationRule) { this.insertBoundaryPoint(argIndex, coord); } else { this.insertPoint(argIndex, coord, loc); }
111030           };
111031           GeometryGraph.prototype.addPolygonRing = function addPolygonRing (lr, cwLeft, cwRight) {
111032             if (lr.isEmpty()) { return null }
111033             var coord = CoordinateArrays.removeRepeatedPoints(lr.getCoordinates());
111034             if (coord.length < 4) {
111035               this._hasTooFewPoints = true;
111036               this._invalidPoint = coord[0];
111037               return null
111038             }
111039             var left = cwLeft;
111040             var right = cwRight;
111041             if (CGAlgorithms.isCCW(coord)) {
111042               left = cwRight;
111043               right = cwLeft;
111044             }
111045             var e = new Edge(coord, new Label(this._argIndex, Location.BOUNDARY, left, right));
111046             this._lineEdgeMap.put(lr, e);
111047             this.insertEdge(e);
111048             this.insertPoint(this._argIndex, coord[0], Location.BOUNDARY);
111049           };
111050           GeometryGraph.prototype.insertPoint = function insertPoint (argIndex, coord, onLocation) {
111051             var n = this._nodes.addNode(coord);
111052             var lbl = n.getLabel();
111053             if (lbl === null) {
111054               n._label = new Label(argIndex, onLocation);
111055             } else { lbl.setLocation(argIndex, onLocation); }
111056           };
111057           GeometryGraph.prototype.createEdgeSetIntersector = function createEdgeSetIntersector () {
111058             return new SimpleMCSweepLineIntersector()
111059           };
111060           GeometryGraph.prototype.addSelfIntersectionNodes = function addSelfIntersectionNodes (argIndex) {
111061             var this$1 = this;
111062
111063             for (var i = this._edges.iterator(); i.hasNext();) {
111064               var e = i.next();
111065               var eLoc = e.getLabel().getLocation(argIndex);
111066               for (var eiIt = e.eiList.iterator(); eiIt.hasNext();) {
111067                 var ei = eiIt.next();
111068                 this$1.addSelfIntersectionNode(argIndex, ei.coord, eLoc);
111069               }
111070             }
111071           };
111072           GeometryGraph.prototype.add = function add () {
111073             if (arguments.length === 1) {
111074               var g = arguments[0];
111075               if (g.isEmpty()) { return null }
111076               if (g instanceof MultiPolygon) { this._useBoundaryDeterminationRule = false; }
111077               if (g instanceof Polygon) { this.addPolygon(g); }
111078               else if (g instanceof LineString) { this.addLineString(g); }
111079               else if (g instanceof Point$1) { this.addPoint(g); }
111080               else if (g instanceof MultiPoint) { this.addCollection(g); }
111081               else if (g instanceof MultiLineString) { this.addCollection(g); }
111082               else if (g instanceof MultiPolygon) { this.addCollection(g); }
111083               else if (g instanceof GeometryCollection) { this.addCollection(g); }
111084               else { throw new Error(g.getClass().getName()) }
111085             } else { return PlanarGraph$$1.prototype.add.apply(this, arguments) }
111086           };
111087           GeometryGraph.prototype.addCollection = function addCollection (gc) {
111088             var this$1 = this;
111089
111090             for (var i = 0; i < gc.getNumGeometries(); i++) {
111091               var g = gc.getGeometryN(i);
111092               this$1.add(g);
111093             }
111094           };
111095           GeometryGraph.prototype.locate = function locate (pt) {
111096             if (hasInterface(this._parentGeom, Polygonal) && this._parentGeom.getNumGeometries() > 50) {
111097               if (this._areaPtLocator === null) {
111098                 this._areaPtLocator = new IndexedPointInAreaLocator(this._parentGeom);
111099               }
111100               return this._areaPtLocator.locate(pt)
111101             }
111102             return this._ptLocator.locate(pt, this._parentGeom)
111103           };
111104           GeometryGraph.prototype.findEdge = function findEdge () {
111105             if (arguments.length === 1) {
111106               var line = arguments[0];
111107               return this._lineEdgeMap.get(line)
111108             } else { return PlanarGraph$$1.prototype.findEdge.apply(this, arguments) }
111109           };
111110           GeometryGraph.prototype.interfaces_ = function interfaces_ () {
111111             return []
111112           };
111113           GeometryGraph.prototype.getClass = function getClass () {
111114             return GeometryGraph
111115           };
111116           GeometryGraph.determineBoundary = function determineBoundary (boundaryNodeRule, boundaryCount) {
111117             return boundaryNodeRule.isInBoundary(boundaryCount) ? Location.BOUNDARY : Location.INTERIOR
111118           };
111119
111120           return GeometryGraph;
111121         }(PlanarGraph));
111122
111123         var GeometryGraphOp = function GeometryGraphOp () {
111124           this._li = new RobustLineIntersector();
111125           this._resultPrecisionModel = null;
111126           this._arg = null;
111127           if (arguments.length === 1) {
111128             var g0 = arguments[0];
111129             this.setComputationPrecision(g0.getPrecisionModel());
111130             this._arg = new Array(1).fill(null);
111131             this._arg[0] = new GeometryGraph(0, g0);
111132           } else if (arguments.length === 2) {
111133             var g0$1 = arguments[0];
111134             var g1 = arguments[1];
111135             var boundaryNodeRule = BoundaryNodeRule.OGC_SFS_BOUNDARY_RULE;
111136             if (g0$1.getPrecisionModel().compareTo(g1.getPrecisionModel()) >= 0) { this.setComputationPrecision(g0$1.getPrecisionModel()); } else { this.setComputationPrecision(g1.getPrecisionModel()); }
111137             this._arg = new Array(2).fill(null);
111138             this._arg[0] = new GeometryGraph(0, g0$1, boundaryNodeRule);
111139             this._arg[1] = new GeometryGraph(1, g1, boundaryNodeRule);
111140           } else if (arguments.length === 3) {
111141             var g0$2 = arguments[0];
111142             var g1$1 = arguments[1];
111143             var boundaryNodeRule$1 = arguments[2];
111144             if (g0$2.getPrecisionModel().compareTo(g1$1.getPrecisionModel()) >= 0) { this.setComputationPrecision(g0$2.getPrecisionModel()); } else { this.setComputationPrecision(g1$1.getPrecisionModel()); }
111145             this._arg = new Array(2).fill(null);
111146             this._arg[0] = new GeometryGraph(0, g0$2, boundaryNodeRule$1);
111147             this._arg[1] = new GeometryGraph(1, g1$1, boundaryNodeRule$1);
111148           }
111149         };
111150         GeometryGraphOp.prototype.getArgGeometry = function getArgGeometry (i) {
111151           return this._arg[i].getGeometry()
111152         };
111153         GeometryGraphOp.prototype.setComputationPrecision = function setComputationPrecision (pm) {
111154           this._resultPrecisionModel = pm;
111155           this._li.setPrecisionModel(this._resultPrecisionModel);
111156         };
111157         GeometryGraphOp.prototype.interfaces_ = function interfaces_ () {
111158           return []
111159         };
111160         GeometryGraphOp.prototype.getClass = function getClass () {
111161           return GeometryGraphOp
111162         };
111163
111164         // operation.geometrygraph
111165
111166         var GeometryMapper = function GeometryMapper () {};
111167
111168         GeometryMapper.prototype.interfaces_ = function interfaces_ () {
111169           return []
111170         };
111171         GeometryMapper.prototype.getClass = function getClass () {
111172           return GeometryMapper
111173         };
111174         GeometryMapper.map = function map () {
111175           if (arguments[0] instanceof Geometry && hasInterface(arguments[1], GeometryMapper.MapOp)) {
111176             var geom = arguments[0];
111177             var op = arguments[1];
111178             var mapped = new ArrayList();
111179             for (var i = 0; i < geom.getNumGeometries(); i++) {
111180               var g = op.map(geom.getGeometryN(i));
111181               if (g !== null) { mapped.add(g); }
111182             }
111183             return geom.getFactory().buildGeometry(mapped)
111184           } else if (hasInterface(arguments[0], Collection) && hasInterface(arguments[1], GeometryMapper.MapOp)) {
111185             var geoms = arguments[0];
111186             var op$1 = arguments[1];
111187             var mapped$1 = new ArrayList();
111188             for (var i$1 = geoms.iterator(); i$1.hasNext();) {
111189               var g$1 = i$1.next();
111190               var gr = op$1.map(g$1);
111191               if (gr !== null) { mapped$1.add(gr); }
111192             }
111193             return mapped$1
111194           }
111195         };
111196         GeometryMapper.MapOp = function MapOp () {};
111197
111198         var OverlayOp = (function (GeometryGraphOp) {
111199           function OverlayOp () {
111200             var g0 = arguments[0];
111201             var g1 = arguments[1];
111202             GeometryGraphOp.call(this, g0, g1);
111203             this._ptLocator = new PointLocator();
111204             this._geomFact = null;
111205             this._resultGeom = null;
111206             this._graph = null;
111207             this._edgeList = new EdgeList();
111208             this._resultPolyList = new ArrayList();
111209             this._resultLineList = new ArrayList();
111210             this._resultPointList = new ArrayList();
111211             this._graph = new PlanarGraph(new OverlayNodeFactory());
111212             this._geomFact = g0.getFactory();
111213           }
111214
111215           if ( GeometryGraphOp ) { OverlayOp.__proto__ = GeometryGraphOp; }
111216           OverlayOp.prototype = Object.create( GeometryGraphOp && GeometryGraphOp.prototype );
111217           OverlayOp.prototype.constructor = OverlayOp;
111218           OverlayOp.prototype.insertUniqueEdge = function insertUniqueEdge (e) {
111219             var existingEdge = this._edgeList.findEqualEdge(e);
111220             if (existingEdge !== null) {
111221               var existingLabel = existingEdge.getLabel();
111222               var labelToMerge = e.getLabel();
111223               if (!existingEdge.isPointwiseEqual(e)) {
111224                 labelToMerge = new Label(e.getLabel());
111225                 labelToMerge.flip();
111226               }
111227               var depth = existingEdge.getDepth();
111228               if (depth.isNull()) {
111229                 depth.add(existingLabel);
111230               }
111231               depth.add(labelToMerge);
111232               existingLabel.merge(labelToMerge);
111233             } else {
111234               this._edgeList.add(e);
111235             }
111236           };
111237           OverlayOp.prototype.getGraph = function getGraph () {
111238             return this._graph
111239           };
111240           OverlayOp.prototype.cancelDuplicateResultEdges = function cancelDuplicateResultEdges () {
111241             for (var it = this._graph.getEdgeEnds().iterator(); it.hasNext();) {
111242               var de = it.next();
111243               var sym = de.getSym();
111244               if (de.isInResult() && sym.isInResult()) {
111245                 de.setInResult(false);
111246                 sym.setInResult(false);
111247               }
111248             }
111249           };
111250           OverlayOp.prototype.isCoveredByLA = function isCoveredByLA (coord) {
111251             if (this.isCovered(coord, this._resultLineList)) { return true }
111252             if (this.isCovered(coord, this._resultPolyList)) { return true }
111253             return false
111254           };
111255           OverlayOp.prototype.computeGeometry = function computeGeometry (resultPointList, resultLineList, resultPolyList, opcode) {
111256             var geomList = new ArrayList();
111257             geomList.addAll(resultPointList);
111258             geomList.addAll(resultLineList);
111259             geomList.addAll(resultPolyList);
111260             if (geomList.isEmpty()) { return OverlayOp.createEmptyResult(opcode, this._arg[0].getGeometry(), this._arg[1].getGeometry(), this._geomFact) }
111261             return this._geomFact.buildGeometry(geomList)
111262           };
111263           OverlayOp.prototype.mergeSymLabels = function mergeSymLabels () {
111264             for (var nodeit = this._graph.getNodes().iterator(); nodeit.hasNext();) {
111265               var node = nodeit.next();
111266               node.getEdges().mergeSymLabels();
111267             }
111268           };
111269           OverlayOp.prototype.isCovered = function isCovered (coord, geomList) {
111270             var this$1 = this;
111271
111272             for (var it = geomList.iterator(); it.hasNext();) {
111273               var geom = it.next();
111274               var loc = this$1._ptLocator.locate(coord, geom);
111275               if (loc !== Location.EXTERIOR) { return true }
111276             }
111277             return false
111278           };
111279           OverlayOp.prototype.replaceCollapsedEdges = function replaceCollapsedEdges () {
111280             var newEdges = new ArrayList();
111281             for (var it = this._edgeList.iterator(); it.hasNext();) {
111282               var e = it.next();
111283               if (e.isCollapsed()) {
111284                 it.remove();
111285                 newEdges.add(e.getCollapsedEdge());
111286               }
111287             }
111288             this._edgeList.addAll(newEdges);
111289           };
111290           OverlayOp.prototype.updateNodeLabelling = function updateNodeLabelling () {
111291             for (var nodeit = this._graph.getNodes().iterator(); nodeit.hasNext();) {
111292               var node = nodeit.next();
111293               var lbl = node.getEdges().getLabel();
111294               node.getLabel().merge(lbl);
111295             }
111296           };
111297           OverlayOp.prototype.getResultGeometry = function getResultGeometry (overlayOpCode) {
111298             this.computeOverlay(overlayOpCode);
111299             return this._resultGeom
111300           };
111301           OverlayOp.prototype.insertUniqueEdges = function insertUniqueEdges (edges) {
111302             var this$1 = this;
111303
111304             for (var i = edges.iterator(); i.hasNext();) {
111305               var e = i.next();
111306               this$1.insertUniqueEdge(e);
111307             }
111308           };
111309           OverlayOp.prototype.computeOverlay = function computeOverlay (opCode) {
111310             this.copyPoints(0);
111311             this.copyPoints(1);
111312             this._arg[0].computeSelfNodes(this._li, false);
111313             this._arg[1].computeSelfNodes(this._li, false);
111314             this._arg[0].computeEdgeIntersections(this._arg[1], this._li, true);
111315             var baseSplitEdges = new ArrayList();
111316             this._arg[0].computeSplitEdges(baseSplitEdges);
111317             this._arg[1].computeSplitEdges(baseSplitEdges);
111318             // const splitEdges = baseSplitEdges
111319             this.insertUniqueEdges(baseSplitEdges);
111320             this.computeLabelsFromDepths();
111321             this.replaceCollapsedEdges();
111322             EdgeNodingValidator.checkValid(this._edgeList.getEdges());
111323             this._graph.addEdges(this._edgeList.getEdges());
111324             this.computeLabelling();
111325             this.labelIncompleteNodes();
111326             this.findResultAreaEdges(opCode);
111327             this.cancelDuplicateResultEdges();
111328             var polyBuilder = new PolygonBuilder(this._geomFact);
111329             polyBuilder.add(this._graph);
111330             this._resultPolyList = polyBuilder.getPolygons();
111331             var lineBuilder = new LineBuilder(this, this._geomFact, this._ptLocator);
111332             this._resultLineList = lineBuilder.build(opCode);
111333             var pointBuilder = new PointBuilder(this, this._geomFact, this._ptLocator);
111334             this._resultPointList = pointBuilder.build(opCode);
111335             this._resultGeom = this.computeGeometry(this._resultPointList, this._resultLineList, this._resultPolyList, opCode);
111336           };
111337           OverlayOp.prototype.labelIncompleteNode = function labelIncompleteNode (n, targetIndex) {
111338             var loc = this._ptLocator.locate(n.getCoordinate(), this._arg[targetIndex].getGeometry());
111339             n.getLabel().setLocation(targetIndex, loc);
111340           };
111341           OverlayOp.prototype.copyPoints = function copyPoints (argIndex) {
111342             var this$1 = this;
111343
111344             for (var i = this._arg[argIndex].getNodeIterator(); i.hasNext();) {
111345               var graphNode = i.next();
111346               var newNode = this$1._graph.addNode(graphNode.getCoordinate());
111347               newNode.setLabel(argIndex, graphNode.getLabel().getLocation(argIndex));
111348             }
111349           };
111350           OverlayOp.prototype.findResultAreaEdges = function findResultAreaEdges (opCode) {
111351             for (var it = this._graph.getEdgeEnds().iterator(); it.hasNext();) {
111352               var de = it.next();
111353               var label = de.getLabel();
111354               if (label.isArea() && !de.isInteriorAreaEdge() && OverlayOp.isResultOfOp(label.getLocation(0, Position.RIGHT), label.getLocation(1, Position.RIGHT), opCode)) {
111355                 de.setInResult(true);
111356               }
111357             }
111358           };
111359           OverlayOp.prototype.computeLabelsFromDepths = function computeLabelsFromDepths () {
111360             for (var it = this._edgeList.iterator(); it.hasNext();) {
111361               var e = it.next();
111362               var lbl = e.getLabel();
111363               var depth = e.getDepth();
111364               if (!depth.isNull()) {
111365                 depth.normalize();
111366                 for (var i = 0; i < 2; i++) {
111367                   if (!lbl.isNull(i) && lbl.isArea() && !depth.isNull(i)) {
111368                     if (depth.getDelta(i) === 0) {
111369                       lbl.toLine(i);
111370                     } else {
111371                       Assert.isTrue(!depth.isNull(i, Position.LEFT), 'depth of LEFT side has not been initialized');
111372                       lbl.setLocation(i, Position.LEFT, depth.getLocation(i, Position.LEFT));
111373                       Assert.isTrue(!depth.isNull(i, Position.RIGHT), 'depth of RIGHT side has not been initialized');
111374                       lbl.setLocation(i, Position.RIGHT, depth.getLocation(i, Position.RIGHT));
111375                     }
111376                   }
111377                 }
111378               }
111379             }
111380           };
111381           OverlayOp.prototype.computeLabelling = function computeLabelling () {
111382             var this$1 = this;
111383
111384             for (var nodeit = this._graph.getNodes().iterator(); nodeit.hasNext();) {
111385               var node = nodeit.next();
111386               node.getEdges().computeLabelling(this$1._arg);
111387             }
111388             this.mergeSymLabels();
111389             this.updateNodeLabelling();
111390           };
111391           OverlayOp.prototype.labelIncompleteNodes = function labelIncompleteNodes () {
111392             var this$1 = this;
111393
111394             // let nodeCount = 0
111395             for (var ni = this._graph.getNodes().iterator(); ni.hasNext();) {
111396               var n = ni.next();
111397               var label = n.getLabel();
111398               if (n.isIsolated()) {
111399                 // nodeCount++
111400                 if (label.isNull(0)) { this$1.labelIncompleteNode(n, 0); } else { this$1.labelIncompleteNode(n, 1); }
111401               }
111402               n.getEdges().updateLabelling(label);
111403             }
111404           };
111405           OverlayOp.prototype.isCoveredByA = function isCoveredByA (coord) {
111406             if (this.isCovered(coord, this._resultPolyList)) { return true }
111407             return false
111408           };
111409           OverlayOp.prototype.interfaces_ = function interfaces_ () {
111410             return []
111411           };
111412           OverlayOp.prototype.getClass = function getClass () {
111413             return OverlayOp
111414           };
111415
111416           return OverlayOp;
111417         }(GeometryGraphOp));
111418
111419         OverlayOp.overlayOp = function (geom0, geom1, opCode) {
111420           var gov = new OverlayOp(geom0, geom1);
111421           var geomOv = gov.getResultGeometry(opCode);
111422           return geomOv
111423         };
111424         OverlayOp.intersection = function (g, other) {
111425           if (g.isEmpty() || other.isEmpty()) { return OverlayOp.createEmptyResult(OverlayOp.INTERSECTION, g, other, g.getFactory()) }
111426           if (g.isGeometryCollection()) {
111427             var g2 = other;
111428             return GeometryCollectionMapper.map(g, {
111429               interfaces_: function () {
111430                 return [GeometryMapper.MapOp]
111431               },
111432               map: function (g) {
111433                 return g.intersection(g2)
111434               }
111435             })
111436           }
111437           g.checkNotGeometryCollection(g);
111438           g.checkNotGeometryCollection(other);
111439           return SnapIfNeededOverlayOp.overlayOp(g, other, OverlayOp.INTERSECTION)
111440         };
111441         OverlayOp.symDifference = function (g, other) {
111442           if (g.isEmpty() || other.isEmpty()) {
111443             if (g.isEmpty() && other.isEmpty()) { return OverlayOp.createEmptyResult(OverlayOp.SYMDIFFERENCE, g, other, g.getFactory()) }
111444             if (g.isEmpty()) { return other.copy() }
111445             if (other.isEmpty()) { return g.copy() }
111446           }
111447           g.checkNotGeometryCollection(g);
111448           g.checkNotGeometryCollection(other);
111449           return SnapIfNeededOverlayOp.overlayOp(g, other, OverlayOp.SYMDIFFERENCE)
111450         };
111451         OverlayOp.resultDimension = function (opCode, g0, g1) {
111452           var dim0 = g0.getDimension();
111453           var dim1 = g1.getDimension();
111454           var resultDimension = -1;
111455           switch (opCode) {
111456             case OverlayOp.INTERSECTION:
111457               resultDimension = Math.min(dim0, dim1);
111458               break
111459             case OverlayOp.UNION:
111460               resultDimension = Math.max(dim0, dim1);
111461               break
111462             case OverlayOp.DIFFERENCE:
111463               resultDimension = dim0;
111464               break
111465             case OverlayOp.SYMDIFFERENCE:
111466               resultDimension = Math.max(dim0, dim1);
111467               break
111468           }
111469           return resultDimension
111470         };
111471         OverlayOp.createEmptyResult = function (overlayOpCode, a, b, geomFact) {
111472           var result = null;
111473           switch (OverlayOp.resultDimension(overlayOpCode, a, b)) {
111474             case -1:
111475               result = geomFact.createGeometryCollection(new Array(0).fill(null));
111476               break
111477             case 0:
111478               result = geomFact.createPoint();
111479               break
111480             case 1:
111481               result = geomFact.createLineString();
111482               break
111483             case 2:
111484               result = geomFact.createPolygon();
111485               break
111486           }
111487           return result
111488         };
111489         OverlayOp.difference = function (g, other) {
111490           if (g.isEmpty()) { return OverlayOp.createEmptyResult(OverlayOp.DIFFERENCE, g, other, g.getFactory()) }
111491           if (other.isEmpty()) { return g.copy() }
111492           g.checkNotGeometryCollection(g);
111493           g.checkNotGeometryCollection(other);
111494           return SnapIfNeededOverlayOp.overlayOp(g, other, OverlayOp.DIFFERENCE)
111495         };
111496         OverlayOp.isResultOfOp = function () {
111497           if (arguments.length === 2) {
111498             var label = arguments[0];
111499             var opCode = arguments[1];
111500             var loc0 = label.getLocation(0);
111501             var loc1 = label.getLocation(1);
111502             return OverlayOp.isResultOfOp(loc0, loc1, opCode)
111503           } else if (arguments.length === 3) {
111504             var loc0$1 = arguments[0];
111505             var loc1$1 = arguments[1];
111506             var overlayOpCode = arguments[2];
111507             if (loc0$1 === Location.BOUNDARY) { loc0$1 = Location.INTERIOR; }
111508             if (loc1$1 === Location.BOUNDARY) { loc1$1 = Location.INTERIOR; }
111509             switch (overlayOpCode) {
111510               case OverlayOp.INTERSECTION:
111511                 return loc0$1 === Location.INTERIOR && loc1$1 === Location.INTERIOR
111512               case OverlayOp.UNION:
111513                 return loc0$1 === Location.INTERIOR || loc1$1 === Location.INTERIOR
111514               case OverlayOp.DIFFERENCE:
111515                 return loc0$1 === Location.INTERIOR && loc1$1 !== Location.INTERIOR
111516               case OverlayOp.SYMDIFFERENCE:
111517                 return (loc0$1 === Location.INTERIOR && loc1$1 !== Location.INTERIOR) || (loc0$1 !== Location.INTERIOR && loc1$1 === Location.INTERIOR)
111518             }
111519             return false
111520           }
111521         };
111522         OverlayOp.INTERSECTION = 1;
111523         OverlayOp.UNION = 2;
111524         OverlayOp.DIFFERENCE = 3;
111525         OverlayOp.SYMDIFFERENCE = 4;
111526
111527         var FuzzyPointLocator = function FuzzyPointLocator () {
111528           this._g = null;
111529           this._boundaryDistanceTolerance = null;
111530           this._linework = null;
111531           this._ptLocator = new PointLocator();
111532           this._seg = new LineSegment();
111533           var g = arguments[0];
111534           var boundaryDistanceTolerance = arguments[1];
111535           this._g = g;
111536           this._boundaryDistanceTolerance = boundaryDistanceTolerance;
111537           this._linework = this.extractLinework(g);
111538         };
111539         FuzzyPointLocator.prototype.isWithinToleranceOfBoundary = function isWithinToleranceOfBoundary (pt) {
111540             var this$1 = this;
111541
111542           for (var i = 0; i < this._linework.getNumGeometries(); i++) {
111543             var line = this$1._linework.getGeometryN(i);
111544             var seq = line.getCoordinateSequence();
111545             for (var j = 0; j < seq.size() - 1; j++) {
111546               seq.getCoordinate(j, this$1._seg.p0);
111547               seq.getCoordinate(j + 1, this$1._seg.p1);
111548               var dist = this$1._seg.distance(pt);
111549               if (dist <= this$1._boundaryDistanceTolerance) { return true }
111550             }
111551           }
111552           return false
111553         };
111554         FuzzyPointLocator.prototype.getLocation = function getLocation (pt) {
111555           if (this.isWithinToleranceOfBoundary(pt)) { return Location.BOUNDARY }
111556           return this._ptLocator.locate(pt, this._g)
111557         };
111558         FuzzyPointLocator.prototype.extractLinework = function extractLinework (g) {
111559           var extracter = new PolygonalLineworkExtracter();
111560           g.apply(extracter);
111561           var linework = extracter.getLinework();
111562           var lines = GeometryFactory.toLineStringArray(linework);
111563           return g.getFactory().createMultiLineString(lines)
111564         };
111565         FuzzyPointLocator.prototype.interfaces_ = function interfaces_ () {
111566           return []
111567         };
111568         FuzzyPointLocator.prototype.getClass = function getClass () {
111569           return FuzzyPointLocator
111570         };
111571
111572         var PolygonalLineworkExtracter = function PolygonalLineworkExtracter () {
111573           this._linework = null;
111574           this._linework = new ArrayList();
111575         };
111576         PolygonalLineworkExtracter.prototype.getLinework = function getLinework () {
111577           return this._linework
111578         };
111579         PolygonalLineworkExtracter.prototype.filter = function filter (g) {
111580             var this$1 = this;
111581
111582           if (g instanceof Polygon) {
111583             var poly = g;
111584             this._linework.add(poly.getExteriorRing());
111585             for (var i = 0; i < poly.getNumInteriorRing(); i++) {
111586               this$1._linework.add(poly.getInteriorRingN(i));
111587             }
111588           }
111589         };
111590         PolygonalLineworkExtracter.prototype.interfaces_ = function interfaces_ () {
111591           return [GeometryFilter]
111592         };
111593         PolygonalLineworkExtracter.prototype.getClass = function getClass () {
111594           return PolygonalLineworkExtracter
111595         };
111596
111597         var OffsetPointGenerator = function OffsetPointGenerator () {
111598           this._g = null;
111599           this._doLeft = true;
111600           this._doRight = true;
111601           var g = arguments[0];
111602           this._g = g;
111603         };
111604         OffsetPointGenerator.prototype.extractPoints = function extractPoints (line, offsetDistance, offsetPts) {
111605             var this$1 = this;
111606
111607           var pts = line.getCoordinates();
111608           for (var i = 0; i < pts.length - 1; i++) {
111609             this$1.computeOffsetPoints(pts[i], pts[i + 1], offsetDistance, offsetPts);
111610           }
111611         };
111612         OffsetPointGenerator.prototype.setSidesToGenerate = function setSidesToGenerate (doLeft, doRight) {
111613           this._doLeft = doLeft;
111614           this._doRight = doRight;
111615         };
111616         OffsetPointGenerator.prototype.getPoints = function getPoints (offsetDistance) {
111617             var this$1 = this;
111618
111619           var offsetPts = new ArrayList();
111620           var lines = LinearComponentExtracter.getLines(this._g);
111621           for (var i = lines.iterator(); i.hasNext();) {
111622             var line = i.next();
111623             this$1.extractPoints(line, offsetDistance, offsetPts);
111624           }
111625           return offsetPts
111626         };
111627         OffsetPointGenerator.prototype.computeOffsetPoints = function computeOffsetPoints (p0, p1, offsetDistance, offsetPts) {
111628           var dx = p1.x - p0.x;
111629           var dy = p1.y - p0.y;
111630           var len = Math.sqrt(dx * dx + dy * dy);
111631           var ux = offsetDistance * dx / len;
111632           var uy = offsetDistance * dy / len;
111633           var midX = (p1.x + p0.x) / 2;
111634           var midY = (p1.y + p0.y) / 2;
111635           if (this._doLeft) {
111636             var offsetLeft = new Coordinate(midX - uy, midY + ux);
111637             offsetPts.add(offsetLeft);
111638           }
111639           if (this._doRight) {
111640             var offsetRight = new Coordinate(midX + uy, midY - ux);
111641             offsetPts.add(offsetRight);
111642           }
111643         };
111644         OffsetPointGenerator.prototype.interfaces_ = function interfaces_ () {
111645           return []
111646         };
111647         OffsetPointGenerator.prototype.getClass = function getClass () {
111648           return OffsetPointGenerator
111649         };
111650
111651         var OverlayResultValidator = function OverlayResultValidator () {
111652           this._geom = null;
111653           this._locFinder = null;
111654           this._location = new Array(3).fill(null);
111655           this._invalidLocation = null;
111656           this._boundaryDistanceTolerance = OverlayResultValidator.TOLERANCE;
111657           this._testCoords = new ArrayList();
111658           var a = arguments[0];
111659           var b = arguments[1];
111660           var result = arguments[2];
111661           this._boundaryDistanceTolerance = OverlayResultValidator.computeBoundaryDistanceTolerance(a, b);
111662           this._geom = [a, b, result];
111663           this._locFinder = [new FuzzyPointLocator(this._geom[0], this._boundaryDistanceTolerance), new FuzzyPointLocator(this._geom[1], this._boundaryDistanceTolerance), new FuzzyPointLocator(this._geom[2], this._boundaryDistanceTolerance)];
111664         };
111665
111666         var staticAccessors$46 = { TOLERANCE: { configurable: true } };
111667         OverlayResultValidator.prototype.reportResult = function reportResult (overlayOp, location, expectedInterior) {
111668           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]));
111669         };
111670         OverlayResultValidator.prototype.isValid = function isValid (overlayOp) {
111671           this.addTestPts(this._geom[0]);
111672           this.addTestPts(this._geom[1]);
111673           var isValid = this.checkValid(overlayOp);
111674           return isValid
111675         };
111676         OverlayResultValidator.prototype.checkValid = function checkValid () {
111677             var this$1 = this;
111678
111679           if (arguments.length === 1) {
111680             var overlayOp = arguments[0];
111681             for (var i = 0; i < this._testCoords.size(); i++) {
111682               var pt = this$1._testCoords.get(i);
111683               if (!this$1.checkValid(overlayOp, pt)) {
111684                 this$1._invalidLocation = pt;
111685                 return false
111686               }
111687             }
111688             return true
111689           } else if (arguments.length === 2) {
111690             var overlayOp$1 = arguments[0];
111691             var pt$1 = arguments[1];
111692             this._location[0] = this._locFinder[0].getLocation(pt$1);
111693             this._location[1] = this._locFinder[1].getLocation(pt$1);
111694             this._location[2] = this._locFinder[2].getLocation(pt$1);
111695             if (OverlayResultValidator.hasLocation(this._location, Location.BOUNDARY)) { return true }
111696             return this.isValidResult(overlayOp$1, this._location)
111697           }
111698         };
111699         OverlayResultValidator.prototype.addTestPts = function addTestPts (g) {
111700           var ptGen = new OffsetPointGenerator(g);
111701           this._testCoords.addAll(ptGen.getPoints(5 * this._boundaryDistanceTolerance));
111702         };
111703         OverlayResultValidator.prototype.isValidResult = function isValidResult (overlayOp, location) {
111704           var expectedInterior = OverlayOp.isResultOfOp(location[0], location[1], overlayOp);
111705           var resultInInterior = location[2] === Location.INTERIOR;
111706           var isValid = !(expectedInterior ^ resultInInterior);
111707           if (!isValid) { this.reportResult(overlayOp, location, expectedInterior); }
111708           return isValid
111709         };
111710         OverlayResultValidator.prototype.getInvalidLocation = function getInvalidLocation () {
111711           return this._invalidLocation
111712         };
111713         OverlayResultValidator.prototype.interfaces_ = function interfaces_ () {
111714           return []
111715         };
111716         OverlayResultValidator.prototype.getClass = function getClass () {
111717           return OverlayResultValidator
111718         };
111719         OverlayResultValidator.hasLocation = function hasLocation (location, loc) {
111720           for (var i = 0; i < 3; i++) {
111721             if (location[i] === loc) { return true }
111722           }
111723           return false
111724         };
111725         OverlayResultValidator.computeBoundaryDistanceTolerance = function computeBoundaryDistanceTolerance (g0, g1) {
111726           return Math.min(GeometrySnapper.computeSizeBasedSnapTolerance(g0), GeometrySnapper.computeSizeBasedSnapTolerance(g1))
111727         };
111728         OverlayResultValidator.isValid = function isValid (a, b, overlayOp, result) {
111729           var validator = new OverlayResultValidator(a, b, result);
111730           return validator.isValid(overlayOp)
111731         };
111732         staticAccessors$46.TOLERANCE.get = function () { return 0.000001 };
111733
111734         Object.defineProperties( OverlayResultValidator, staticAccessors$46 );
111735
111736         // operation.overlay
111737
111738         var GeometryCombiner = function GeometryCombiner (geoms) {
111739           this._geomFactory = null;
111740           this._skipEmpty = false;
111741           this._inputGeoms = null;
111742           this._geomFactory = GeometryCombiner.extractFactory(geoms);
111743           this._inputGeoms = geoms;
111744         };
111745         GeometryCombiner.prototype.extractElements = function extractElements (geom, elems) {
111746             var this$1 = this;
111747
111748           if (geom === null) { return null }
111749           for (var i = 0; i < geom.getNumGeometries(); i++) {
111750             var elemGeom = geom.getGeometryN(i);
111751             if (this$1._skipEmpty && elemGeom.isEmpty()) { continue }
111752             elems.add(elemGeom);
111753           }
111754         };
111755         GeometryCombiner.prototype.combine = function combine () {
111756             var this$1 = this;
111757
111758           var elems = new ArrayList();
111759           for (var i = this._inputGeoms.iterator(); i.hasNext();) {
111760             var g = i.next();
111761             this$1.extractElements(g, elems);
111762           }
111763           if (elems.size() === 0) {
111764             if (this._geomFactory !== null) {
111765               return this._geomFactory.createGeometryCollection(null)
111766             }
111767             return null
111768           }
111769           return this._geomFactory.buildGeometry(elems)
111770         };
111771         GeometryCombiner.prototype.interfaces_ = function interfaces_ () {
111772           return []
111773         };
111774         GeometryCombiner.prototype.getClass = function getClass () {
111775           return GeometryCombiner
111776         };
111777         GeometryCombiner.combine = function combine () {
111778           if (arguments.length === 1) {
111779             var geoms = arguments[0];
111780             var combiner = new GeometryCombiner(geoms);
111781             return combiner.combine()
111782           } else if (arguments.length === 2) {
111783             var g0 = arguments[0];
111784             var g1 = arguments[1];
111785             var combiner$1 = new GeometryCombiner(GeometryCombiner.createList(g0, g1));
111786             return combiner$1.combine()
111787           } else if (arguments.length === 3) {
111788             var g0$1 = arguments[0];
111789             var g1$1 = arguments[1];
111790             var g2 = arguments[2];
111791             var combiner$2 = new GeometryCombiner(GeometryCombiner.createList(g0$1, g1$1, g2));
111792             return combiner$2.combine()
111793           }
111794         };
111795         GeometryCombiner.extractFactory = function extractFactory (geoms) {
111796           if (geoms.isEmpty()) { return null }
111797           return geoms.iterator().next().getFactory()
111798         };
111799         GeometryCombiner.createList = function createList () {
111800           if (arguments.length === 2) {
111801             var obj0 = arguments[0];
111802             var obj1 = arguments[1];
111803             var list = new ArrayList();
111804             list.add(obj0);
111805             list.add(obj1);
111806             return list
111807           } else if (arguments.length === 3) {
111808             var obj0$1 = arguments[0];
111809             var obj1$1 = arguments[1];
111810             var obj2 = arguments[2];
111811             var list$1 = new ArrayList();
111812             list$1.add(obj0$1);
111813             list$1.add(obj1$1);
111814             list$1.add(obj2);
111815             return list$1
111816           }
111817         };
111818
111819         var CascadedPolygonUnion = function CascadedPolygonUnion () {
111820           this._inputPolys = null;
111821           this._geomFactory = null;
111822           var polys = arguments[0];
111823           this._inputPolys = polys;
111824           if (this._inputPolys === null) { this._inputPolys = new ArrayList(); }
111825         };
111826
111827         var staticAccessors$47 = { STRTREE_NODE_CAPACITY: { configurable: true } };
111828         CascadedPolygonUnion.prototype.reduceToGeometries = function reduceToGeometries (geomTree) {
111829             var this$1 = this;
111830
111831           var geoms = new ArrayList();
111832           for (var i = geomTree.iterator(); i.hasNext();) {
111833             var o = i.next();
111834             var geom = null;
111835             if (hasInterface(o, List)) {
111836               geom = this$1.unionTree(o);
111837             } else if (o instanceof Geometry) {
111838               geom = o;
111839             }
111840             geoms.add(geom);
111841           }
111842           return geoms
111843         };
111844         CascadedPolygonUnion.prototype.extractByEnvelope = function extractByEnvelope (env, geom, disjointGeoms) {
111845           var intersectingGeoms = new ArrayList();
111846           for (var i = 0; i < geom.getNumGeometries(); i++) {
111847             var elem = geom.getGeometryN(i);
111848             if (elem.getEnvelopeInternal().intersects(env)) { intersectingGeoms.add(elem); } else { disjointGeoms.add(elem); }
111849           }
111850           return this._geomFactory.buildGeometry(intersectingGeoms)
111851         };
111852         CascadedPolygonUnion.prototype.unionOptimized = function unionOptimized (g0, g1) {
111853           var g0Env = g0.getEnvelopeInternal();
111854           var g1Env = g1.getEnvelopeInternal();
111855           if (!g0Env.intersects(g1Env)) {
111856             var combo = GeometryCombiner.combine(g0, g1);
111857             return combo
111858           }
111859           if (g0.getNumGeometries() <= 1 && g1.getNumGeometries() <= 1) { return this.unionActual(g0, g1) }
111860           var commonEnv = g0Env.intersection(g1Env);
111861           return this.unionUsingEnvelopeIntersection(g0, g1, commonEnv)
111862         };
111863         CascadedPolygonUnion.prototype.union = function union () {
111864           if (this._inputPolys === null) { throw new Error('union() method cannot be called twice') }
111865           if (this._inputPolys.isEmpty()) { return null }
111866           this._geomFactory = this._inputPolys.iterator().next().getFactory();
111867           var index = new STRtree(CascadedPolygonUnion.STRTREE_NODE_CAPACITY);
111868           for (var i = this._inputPolys.iterator(); i.hasNext();) {
111869             var item = i.next();
111870             index.insert(item.getEnvelopeInternal(), item);
111871           }
111872           this._inputPolys = null;
111873           var itemTree = index.itemsTree();
111874           var unionAll = this.unionTree(itemTree);
111875           return unionAll
111876         };
111877         CascadedPolygonUnion.prototype.binaryUnion = function binaryUnion () {
111878           if (arguments.length === 1) {
111879             var geoms = arguments[0];
111880             return this.binaryUnion(geoms, 0, geoms.size())
111881           } else if (arguments.length === 3) {
111882             var geoms$1 = arguments[0];
111883             var start = arguments[1];
111884             var end = arguments[2];
111885             if (end - start <= 1) {
111886               var g0 = CascadedPolygonUnion.getGeometry(geoms$1, start);
111887               return this.unionSafe(g0, null)
111888             } else if (end - start === 2) {
111889               return this.unionSafe(CascadedPolygonUnion.getGeometry(geoms$1, start), CascadedPolygonUnion.getGeometry(geoms$1, start + 1))
111890             } else {
111891               var mid = Math.trunc((end + start) / 2);
111892               var g0$1 = this.binaryUnion(geoms$1, start, mid);
111893               var g1 = this.binaryUnion(geoms$1, mid, end);
111894               return this.unionSafe(g0$1, g1)
111895             }
111896           }
111897         };
111898         CascadedPolygonUnion.prototype.repeatedUnion = function repeatedUnion (geoms) {
111899           var union = null;
111900           for (var i = geoms.iterator(); i.hasNext();) {
111901             var g = i.next();
111902             if (union === null) { union = g.copy(); } else { union = union.union(g); }
111903           }
111904           return union
111905         };
111906         CascadedPolygonUnion.prototype.unionSafe = function unionSafe (g0, g1) {
111907           if (g0 === null && g1 === null) { return null }
111908           if (g0 === null) { return g1.copy() }
111909           if (g1 === null) { return g0.copy() }
111910           return this.unionOptimized(g0, g1)
111911         };
111912         CascadedPolygonUnion.prototype.unionActual = function unionActual (g0, g1) {
111913           return CascadedPolygonUnion.restrictToPolygons(g0.union(g1))
111914         };
111915         CascadedPolygonUnion.prototype.unionTree = function unionTree (geomTree) {
111916           var geoms = this.reduceToGeometries(geomTree);
111917           var union = this.binaryUnion(geoms);
111918           return union
111919         };
111920         CascadedPolygonUnion.prototype.unionUsingEnvelopeIntersection = function unionUsingEnvelopeIntersection (g0, g1, common) {
111921           var disjointPolys = new ArrayList();
111922           var g0Int = this.extractByEnvelope(common, g0, disjointPolys);
111923           var g1Int = this.extractByEnvelope(common, g1, disjointPolys);
111924           var union = this.unionActual(g0Int, g1Int);
111925           disjointPolys.add(union);
111926           var overallUnion = GeometryCombiner.combine(disjointPolys);
111927           return overallUnion
111928         };
111929         CascadedPolygonUnion.prototype.bufferUnion = function bufferUnion () {
111930           if (arguments.length === 1) {
111931             var geoms = arguments[0];
111932             var factory = geoms.get(0).getFactory();
111933             var gColl = factory.buildGeometry(geoms);
111934             var unionAll = gColl.buffer(0.0);
111935             return unionAll
111936           } else if (arguments.length === 2) {
111937             var g0 = arguments[0];
111938             var g1 = arguments[1];
111939             var factory$1 = g0.getFactory();
111940             var gColl$1 = factory$1.createGeometryCollection([g0, g1]);
111941             var unionAll$1 = gColl$1.buffer(0.0);
111942             return unionAll$1
111943           }
111944         };
111945         CascadedPolygonUnion.prototype.interfaces_ = function interfaces_ () {
111946           return []
111947         };
111948         CascadedPolygonUnion.prototype.getClass = function getClass () {
111949           return CascadedPolygonUnion
111950         };
111951         CascadedPolygonUnion.restrictToPolygons = function restrictToPolygons (g) {
111952           if (hasInterface(g, Polygonal)) {
111953             return g
111954           }
111955           var polygons = PolygonExtracter.getPolygons(g);
111956           if (polygons.size() === 1) { return polygons.get(0) }
111957           return g.getFactory().createMultiPolygon(GeometryFactory.toPolygonArray(polygons))
111958         };
111959         CascadedPolygonUnion.getGeometry = function getGeometry (list, index) {
111960           if (index >= list.size()) { return null }
111961           return list.get(index)
111962         };
111963         CascadedPolygonUnion.union = function union (polys) {
111964           var op = new CascadedPolygonUnion(polys);
111965           return op.union()
111966         };
111967         staticAccessors$47.STRTREE_NODE_CAPACITY.get = function () { return 4 };
111968
111969         Object.defineProperties( CascadedPolygonUnion, staticAccessors$47 );
111970
111971         var UnionOp = function UnionOp () {};
111972
111973         UnionOp.prototype.interfaces_ = function interfaces_ () {
111974           return []
111975         };
111976         UnionOp.prototype.getClass = function getClass () {
111977           return UnionOp
111978         };
111979         UnionOp.union = function union (g, other) {
111980           if (g.isEmpty() || other.isEmpty()) {
111981             if (g.isEmpty() && other.isEmpty()) { return OverlayOp.createEmptyResult(OverlayOp.UNION, g, other, g.getFactory()) }
111982             if (g.isEmpty()) { return other.copy() }
111983             if (other.isEmpty()) { return g.copy() }
111984           }
111985           g.checkNotGeometryCollection(g);
111986           g.checkNotGeometryCollection(other);
111987           return SnapIfNeededOverlayOp.overlayOp(g, other, OverlayOp.UNION)
111988         };
111989
111990         /**
111991          * Earth Radius used with the Harvesine formula and approximates using a spherical (non-ellipsoid) Earth.
111992          */
111993
111994         /**
111995          * Wraps a GeoJSON {@link Geometry} in a GeoJSON {@link Feature}.
111996          *
111997          * @name feature
111998          * @param {Geometry} geometry input geometry
111999          * @param {Object} [properties={}] an Object of key-value pairs to add as properties
112000          * @param {Object} [options={}] Optional Parameters
112001          * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
112002          * @param {string|number} [options.id] Identifier associated with the Feature
112003          * @returns {Feature} a GeoJSON Feature
112004          * @example
112005          * var geometry = {
112006          *   "type": "Point",
112007          *   "coordinates": [110, 50]
112008          * };
112009          *
112010          * var feature = turf.feature(geometry);
112011          *
112012          * //=feature
112013          */
112014         function feature$1(geometry, properties, options) {
112015             // Optional Parameters
112016             options = options || {};
112017             if (!isObject$4(options)) { throw new Error('options is invalid'); }
112018             var bbox = options.bbox;
112019             var id = options.id;
112020
112021             // Validation
112022             if (geometry === undefined) { throw new Error('geometry is required'); }
112023             if (properties && properties.constructor !== Object) { throw new Error('properties must be an Object'); }
112024             if (bbox) { validateBBox(bbox); }
112025             if (id) { validateId(id); }
112026
112027             // Main
112028             var feat = {type: 'Feature'};
112029             if (id) { feat.id = id; }
112030             if (bbox) { feat.bbox = bbox; }
112031             feat.properties = properties || {};
112032             feat.geometry = geometry;
112033             return feat;
112034         }
112035
112036         /**
112037          * isNumber
112038          *
112039          * @param {*} num Number to validate
112040          * @returns {boolean} true/false
112041          * @example
112042          * turf.isNumber(123)
112043          * //=true
112044          * turf.isNumber('foo')
112045          * //=false
112046          */
112047         function isNumber$1(num) {
112048             return !isNaN(num) && num !== null && !Array.isArray(num);
112049         }
112050
112051         /**
112052          * isObject
112053          *
112054          * @param {*} input variable to validate
112055          * @returns {boolean} true/false
112056          * @example
112057          * turf.isObject({elevation: 10})
112058          * //=true
112059          * turf.isObject('foo')
112060          * //=false
112061          */
112062         function isObject$4(input) {
112063             return (!!input) && (input.constructor === Object);
112064         }
112065
112066         /**
112067          * Validate BBox
112068          *
112069          * @private
112070          * @param {Array<number>} bbox BBox to validate
112071          * @returns {void}
112072          * @throws Error if BBox is not valid
112073          * @example
112074          * validateBBox([-180, -40, 110, 50])
112075          * //=OK
112076          * validateBBox([-180, -40])
112077          * //=Error
112078          * validateBBox('Foo')
112079          * //=Error
112080          * validateBBox(5)
112081          * //=Error
112082          * validateBBox(null)
112083          * //=Error
112084          * validateBBox(undefined)
112085          * //=Error
112086          */
112087         function validateBBox(bbox) {
112088             if (!bbox) { throw new Error('bbox is required'); }
112089             if (!Array.isArray(bbox)) { throw new Error('bbox must be an Array'); }
112090             if (bbox.length !== 4 && bbox.length !== 6) { throw new Error('bbox must be an Array of 4 or 6 numbers'); }
112091             bbox.forEach(function (num) {
112092                 if (!isNumber$1(num)) { throw new Error('bbox must only contain numbers'); }
112093             });
112094         }
112095
112096         /**
112097          * Validate Id
112098          *
112099          * @private
112100          * @param {string|number} id Id to validate
112101          * @returns {void}
112102          * @throws Error if Id is not valid
112103          * @example
112104          * validateId([-180, -40, 110, 50])
112105          * //=Error
112106          * validateId([-180, -40])
112107          * //=Error
112108          * validateId('Foo')
112109          * //=OK
112110          * validateId(5)
112111          * //=OK
112112          * validateId(null)
112113          * //=Error
112114          * validateId(undefined)
112115          * //=Error
112116          */
112117         function validateId(id) {
112118             if (!id) { throw new Error('id is required'); }
112119             if (['string', 'number'].indexOf(typeof id) === -1) { throw new Error('id must be a number or a string'); }
112120         }
112121
112122         /**
112123          * Callback for geomEach
112124          *
112125          * @callback geomEachCallback
112126          * @param {Geometry} currentGeometry The current Geometry being processed.
112127          * @param {number} featureIndex The current index of the Feature being processed.
112128          * @param {Object} featureProperties The current Feature Properties being processed.
112129          * @param {Array<number>} featureBBox The current Feature BBox being processed.
112130          * @param {number|string} featureId The current Feature Id being processed.
112131          */
112132
112133         /**
112134          * Iterate over each geometry in any GeoJSON object, similar to Array.forEach()
112135          *
112136          * @name geomEach
112137          * @param {FeatureCollection|Feature|Geometry} geojson any GeoJSON object
112138          * @param {Function} callback a method that takes (currentGeometry, featureIndex, featureProperties, featureBBox, featureId)
112139          * @returns {void}
112140          * @example
112141          * var features = turf.featureCollection([
112142          *     turf.point([26, 37], {foo: 'bar'}),
112143          *     turf.point([36, 53], {hello: 'world'})
112144          * ]);
112145          *
112146          * turf.geomEach(features, function (currentGeometry, featureIndex, featureProperties, featureBBox, featureId) {
112147          *   //=currentGeometry
112148          *   //=featureIndex
112149          *   //=featureProperties
112150          *   //=featureBBox
112151          *   //=featureId
112152          * });
112153          */
112154         function geomEach(geojson, callback) {
112155             var i, j, g, geometry, stopG,
112156                 geometryMaybeCollection,
112157                 isGeometryCollection,
112158                 featureProperties,
112159                 featureBBox,
112160                 featureId,
112161                 featureIndex = 0,
112162                 isFeatureCollection = geojson.type === 'FeatureCollection',
112163                 isFeature = geojson.type === 'Feature',
112164                 stop = isFeatureCollection ? geojson.features.length : 1;
112165
112166             // This logic may look a little weird. The reason why it is that way
112167             // is because it's trying to be fast. GeoJSON supports multiple kinds
112168             // of objects at its root: FeatureCollection, Features, Geometries.
112169             // This function has the responsibility of handling all of them, and that
112170             // means that some of the `for` loops you see below actually just don't apply
112171             // to certain inputs. For instance, if you give this just a
112172             // Point geometry, then both loops are short-circuited and all we do
112173             // is gradually rename the input until it's called 'geometry'.
112174             //
112175             // This also aims to allocate as few resources as possible: just a
112176             // few numbers and booleans, rather than any temporary arrays as would
112177             // be required with the normalization approach.
112178             for (i = 0; i < stop; i++) {
112179
112180                 geometryMaybeCollection = (isFeatureCollection ? geojson.features[i].geometry :
112181                     (isFeature ? geojson.geometry : geojson));
112182                 featureProperties = (isFeatureCollection ? geojson.features[i].properties :
112183                     (isFeature ? geojson.properties : {}));
112184                 featureBBox = (isFeatureCollection ? geojson.features[i].bbox :
112185                     (isFeature ? geojson.bbox : undefined));
112186                 featureId = (isFeatureCollection ? geojson.features[i].id :
112187                     (isFeature ? geojson.id : undefined));
112188                 isGeometryCollection = (geometryMaybeCollection) ? geometryMaybeCollection.type === 'GeometryCollection' : false;
112189                 stopG = isGeometryCollection ? geometryMaybeCollection.geometries.length : 1;
112190
112191                 for (g = 0; g < stopG; g++) {
112192                     geometry = isGeometryCollection ?
112193                         geometryMaybeCollection.geometries[g] : geometryMaybeCollection;
112194
112195                     // Handle null Geometry
112196                     if (geometry === null) {
112197                         if (callback(null, featureIndex, featureProperties, featureBBox, featureId) === false) { return false; }
112198                         continue;
112199                     }
112200                     switch (geometry.type) {
112201                     case 'Point':
112202                     case 'LineString':
112203                     case 'MultiPoint':
112204                     case 'Polygon':
112205                     case 'MultiLineString':
112206                     case 'MultiPolygon': {
112207                         if (callback(geometry, featureIndex, featureProperties, featureBBox, featureId) === false) { return false; }
112208                         break;
112209                     }
112210                     case 'GeometryCollection': {
112211                         for (j = 0; j < geometry.geometries.length; j++) {
112212                             if (callback(geometry.geometries[j], featureIndex, featureProperties, featureBBox, featureId) === false) { return false; }
112213                         }
112214                         break;
112215                     }
112216                     default:
112217                         throw new Error('Unknown Geometry Type');
112218                     }
112219                 }
112220                 // Only increase `featureIndex` per each feature
112221                 featureIndex++;
112222             }
112223         }
112224
112225         /**
112226          * Callback for geomReduce
112227          *
112228          * The first time the callback function is called, the values provided as arguments depend
112229          * on whether the reduce method has an initialValue argument.
112230          *
112231          * If an initialValue is provided to the reduce method:
112232          *  - The previousValue argument is initialValue.
112233          *  - The currentValue argument is the value of the first element present in the array.
112234          *
112235          * If an initialValue is not provided:
112236          *  - The previousValue argument is the value of the first element present in the array.
112237          *  - The currentValue argument is the value of the second element present in the array.
112238          *
112239          * @callback geomReduceCallback
112240          * @param {*} previousValue The accumulated value previously returned in the last invocation
112241          * of the callback, or initialValue, if supplied.
112242          * @param {Geometry} currentGeometry The current Geometry being processed.
112243          * @param {number} featureIndex The current index of the Feature being processed.
112244          * @param {Object} featureProperties The current Feature Properties being processed.
112245          * @param {Array<number>} featureBBox The current Feature BBox being processed.
112246          * @param {number|string} featureId The current Feature Id being processed.
112247          */
112248
112249         /**
112250          * Reduce geometry in any GeoJSON object, similar to Array.reduce().
112251          *
112252          * @name geomReduce
112253          * @param {FeatureCollection|Feature|Geometry} geojson any GeoJSON object
112254          * @param {Function} callback a method that takes (previousValue, currentGeometry, featureIndex, featureProperties, featureBBox, featureId)
112255          * @param {*} [initialValue] Value to use as the first argument to the first call of the callback.
112256          * @returns {*} The value that results from the reduction.
112257          * @example
112258          * var features = turf.featureCollection([
112259          *     turf.point([26, 37], {foo: 'bar'}),
112260          *     turf.point([36, 53], {hello: 'world'})
112261          * ]);
112262          *
112263          * turf.geomReduce(features, function (previousValue, currentGeometry, featureIndex, featureProperties, featureBBox, featureId) {
112264          *   //=previousValue
112265          *   //=currentGeometry
112266          *   //=featureIndex
112267          *   //=featureProperties
112268          *   //=featureBBox
112269          *   //=featureId
112270          *   return currentGeometry
112271          * });
112272          */
112273         function geomReduce(geojson, callback, initialValue) {
112274             var previousValue = initialValue;
112275             geomEach(geojson, function (currentGeometry, featureIndex, featureProperties, featureBBox, featureId) {
112276                 if (featureIndex === 0 && initialValue === undefined) { previousValue = currentGeometry; }
112277                 else { previousValue = callback(previousValue, currentGeometry, featureIndex, featureProperties, featureBBox, featureId); }
112278             });
112279             return previousValue;
112280         }
112281
112282         /**
112283          * Callback for flattenEach
112284          *
112285          * @callback flattenEachCallback
112286          * @param {Feature} currentFeature The current flattened feature being processed.
112287          * @param {number} featureIndex The current index of the Feature being processed.
112288          * @param {number} multiFeatureIndex The current index of the Multi-Feature being processed.
112289          */
112290
112291         /**
112292          * Iterate over flattened features in any GeoJSON object, similar to
112293          * Array.forEach.
112294          *
112295          * @name flattenEach
112296          * @param {FeatureCollection|Feature|Geometry} geojson any GeoJSON object
112297          * @param {Function} callback a method that takes (currentFeature, featureIndex, multiFeatureIndex)
112298          * @example
112299          * var features = turf.featureCollection([
112300          *     turf.point([26, 37], {foo: 'bar'}),
112301          *     turf.multiPoint([[40, 30], [36, 53]], {hello: 'world'})
112302          * ]);
112303          *
112304          * turf.flattenEach(features, function (currentFeature, featureIndex, multiFeatureIndex) {
112305          *   //=currentFeature
112306          *   //=featureIndex
112307          *   //=multiFeatureIndex
112308          * });
112309          */
112310         function flattenEach(geojson, callback) {
112311             geomEach(geojson, function (geometry, featureIndex, properties, bbox, id) {
112312                 // Callback for single geometry
112313                 var type = (geometry === null) ? null : geometry.type;
112314                 switch (type) {
112315                 case null:
112316                 case 'Point':
112317                 case 'LineString':
112318                 case 'Polygon':
112319                     if (callback(feature$1(geometry, properties, {bbox: bbox, id: id}), featureIndex, 0) === false) { return false; }
112320                     return;
112321                 }
112322
112323                 var geomType;
112324
112325                 // Callback for multi-geometry
112326                 switch (type) {
112327                 case 'MultiPoint':
112328                     geomType = 'Point';
112329                     break;
112330                 case 'MultiLineString':
112331                     geomType = 'LineString';
112332                     break;
112333                 case 'MultiPolygon':
112334                     geomType = 'Polygon';
112335                     break;
112336                 }
112337
112338                 for (var multiFeatureIndex = 0; multiFeatureIndex < geometry.coordinates.length; multiFeatureIndex++) {
112339                     var coordinate = geometry.coordinates[multiFeatureIndex];
112340                     var geom = {
112341                         type: geomType,
112342                         coordinates: coordinate
112343                     };
112344                     if (callback(feature$1(geom, properties), featureIndex, multiFeatureIndex) === false) { return false; }
112345                 }
112346             });
112347         }
112348
112349         /**
112350          * Takes one or more features and returns their area in square meters.
112351          *
112352          * @name area
112353          * @param {GeoJSON} geojson input GeoJSON feature(s)
112354          * @returns {number} area in square meters
112355          * @example
112356          * var polygon = turf.polygon([[[125, -15], [113, -22], [154, -27], [144, -15], [125, -15]]]);
112357          *
112358          * var area = turf.area(polygon);
112359          *
112360          * //addToMap
112361          * var addToMap = [polygon]
112362          * polygon.properties.area = area
112363          */
112364         function area(geojson) {
112365             return geomReduce(geojson, function (value, geom) {
112366                 return value + calculateArea(geom);
112367             }, 0);
112368         }
112369
112370         var RADIUS$1 = 6378137;
112371         // var FLATTENING_DENOM = 298.257223563;
112372         // var FLATTENING = 1 / FLATTENING_DENOM;
112373         // var POLAR_RADIUS = RADIUS * (1 - FLATTENING);
112374
112375         /**
112376          * Calculate Area
112377          *
112378          * @private
112379          * @param {GeoJSON} geojson GeoJSON
112380          * @returns {number} area
112381          */
112382         function calculateArea(geojson) {
112383             var area = 0, i;
112384             switch (geojson.type) {
112385             case 'Polygon':
112386                 return polygonArea$1(geojson.coordinates);
112387             case 'MultiPolygon':
112388                 for (i = 0; i < geojson.coordinates.length; i++) {
112389                     area += polygonArea$1(geojson.coordinates[i]);
112390                 }
112391                 return area;
112392             case 'Point':
112393             case 'MultiPoint':
112394             case 'LineString':
112395             case 'MultiLineString':
112396                 return 0;
112397             case 'GeometryCollection':
112398                 for (i = 0; i < geojson.geometries.length; i++) {
112399                     area += calculateArea(geojson.geometries[i]);
112400                 }
112401                 return area;
112402             }
112403         }
112404
112405         function polygonArea$1(coords) {
112406             var area = 0;
112407             if (coords && coords.length > 0) {
112408                 area += Math.abs(ringArea$1(coords[0]));
112409                 for (var i = 1; i < coords.length; i++) {
112410                     area -= Math.abs(ringArea$1(coords[i]));
112411                 }
112412             }
112413             return area;
112414         }
112415
112416         /**
112417          * @private
112418          * Calculate the approximate area of the polygon were it projected onto the earth.
112419          * Note that this area will be positive if ring is oriented clockwise, otherwise it will be negative.
112420          *
112421          * Reference:
112422          * Robert. G. Chamberlain and William H. Duquette, "Some Algorithms for Polygons on a Sphere", JPL Publication 07-03, Jet Propulsion
112423          * Laboratory, Pasadena, CA, June 2007 http://trs-new.jpl.nasa.gov/dspace/handle/2014/40409
112424          *
112425          * @param {Array<Array<number>>} coords Ring Coordinates
112426          * @returns {number} The approximate signed geodesic area of the polygon in square meters.
112427          */
112428         function ringArea$1(coords) {
112429             var p1;
112430             var p2;
112431             var p3;
112432             var lowerIndex;
112433             var middleIndex;
112434             var upperIndex;
112435             var i;
112436             var area = 0;
112437             var coordsLength = coords.length;
112438
112439             if (coordsLength > 2) {
112440                 for (i = 0; i < coordsLength; i++) {
112441                     if (i === coordsLength - 2) { // i = N-2
112442                         lowerIndex = coordsLength - 2;
112443                         middleIndex = coordsLength - 1;
112444                         upperIndex = 0;
112445                     } else if (i === coordsLength - 1) { // i = N-1
112446                         lowerIndex = coordsLength - 1;
112447                         middleIndex = 0;
112448                         upperIndex = 1;
112449                     } else { // i = 0 to N-3
112450                         lowerIndex = i;
112451                         middleIndex = i + 1;
112452                         upperIndex = i + 2;
112453                     }
112454                     p1 = coords[lowerIndex];
112455                     p2 = coords[middleIndex];
112456                     p3 = coords[upperIndex];
112457                     area += (rad$1(p3[0]) - rad$1(p1[0])) * Math.sin(rad$1(p2[1]));
112458                 }
112459
112460                 area = area * RADIUS$1 * RADIUS$1 / 2;
112461             }
112462
112463             return area;
112464         }
112465
112466         function rad$1(_) {
112467             return _ * Math.PI / 180;
112468         }
112469
112470         /**
112471          * Get Geometry from Feature or Geometry Object
112472          *
112473          * @param {Feature|Geometry} geojson GeoJSON Feature or Geometry Object
112474          * @returns {Geometry|null} GeoJSON Geometry Object
112475          * @throws {Error} if geojson is not a Feature or Geometry Object
112476          * @example
112477          * var point = {
112478          *   "type": "Feature",
112479          *   "properties": {},
112480          *   "geometry": {
112481          *     "type": "Point",
112482          *     "coordinates": [110, 40]
112483          *   }
112484          * }
112485          * var geom = turf.getGeom(point)
112486          * //={"type": "Point", "coordinates": [110, 40]}
112487          */
112488         function getGeom(geojson) {
112489             if (!geojson) { throw new Error('geojson is required'); }
112490             if (geojson.geometry !== undefined) { return geojson.geometry; }
112491             if (geojson.coordinates || geojson.geometries) { return geojson; }
112492             throw new Error('geojson must be a valid Feature or Geometry Object');
112493         }
112494
112495         /**
112496          * Finds the difference between two {@link Polygon|polygons} by clipping the second polygon from the first.
112497          *
112498          * @name difference
112499          * @param {Feature<Polygon|MultiPolygon>} polygon1 input Polygon feature
112500          * @param {Feature<Polygon|MultiPolygon>} polygon2 Polygon feature to difference from polygon1
112501          * @returns {Feature<Polygon|MultiPolygon>|null} a Polygon or MultiPolygon feature showing the area of `polygon1` excluding the area of `polygon2` (if empty returns `null`)
112502          * @example
112503          * var polygon1 = turf.polygon([[
112504          *   [128, -26],
112505          *   [141, -26],
112506          *   [141, -21],
112507          *   [128, -21],
112508          *   [128, -26]
112509          * ]], {
112510          *   "fill": "#F00",
112511          *   "fill-opacity": 0.1
112512          * });
112513          * var polygon2 = turf.polygon([[
112514          *   [126, -28],
112515          *   [140, -28],
112516          *   [140, -20],
112517          *   [126, -20],
112518          *   [126, -28]
112519          * ]], {
112520          *   "fill": "#00F",
112521          *   "fill-opacity": 0.1
112522          * });
112523          *
112524          * var difference = turf.difference(polygon1, polygon2);
112525          *
112526          * //addToMap
112527          * var addToMap = [polygon1, polygon2, difference];
112528          */
112529         function difference(polygon1, polygon2) {
112530             var geom1 = getGeom(polygon1);
112531             var geom2 = getGeom(polygon2);
112532             var properties = polygon1.properties || {};
112533
112534             // Issue #721 - JSTS can't handle empty polygons
112535             geom1 = removeEmptyPolygon(geom1);
112536             geom2 = removeEmptyPolygon(geom2);
112537             if (!geom1) { return null; }
112538             if (!geom2) { return feature$1(geom1, properties); }
112539
112540             // JSTS difference operation
112541             var reader = new GeoJSONReader();
112542             var a = reader.read(geom1);
112543             var b = reader.read(geom2);
112544             var differenced = OverlayOp.difference(a, b);
112545             if (differenced.isEmpty()) { return null; }
112546             var writer = new GeoJSONWriter();
112547             var geom = writer.write(differenced);
112548
112549             return feature$1(geom, properties);
112550         }
112551
112552         /**
112553          * Detect Empty Polygon
112554          *
112555          * @private
112556          * @param {Geometry<Polygon|MultiPolygon>} geom Geometry Object
112557          * @returns {Geometry<Polygon|MultiPolygon>|null} removed any polygons with no areas
112558          */
112559         function removeEmptyPolygon(geom) {
112560             switch (geom.type) {
112561             case 'Polygon':
112562                 if (area(geom) > 1) { return geom; }
112563                 return null;
112564             case 'MultiPolygon':
112565                 var coordinates = [];
112566                 flattenEach(geom, function (feature$$1) {
112567                     if (area(feature$$1) > 1) { coordinates.push(feature$$1.geometry.coordinates); }
112568                 });
112569                 if (coordinates.length) { return {type: 'MultiPolygon', coordinates: coordinates}; }
112570             }
112571         }
112572
112573         /**
112574          * 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.
112575          *
112576          * @name union
112577          * @param {...Feature<Polygon>} A polygon to combine
112578          * @returns {Feature<(Polygon|MultiPolygon)>} a combined {@link Polygon} or {@link MultiPolygon} feature
112579          * @example
112580          * var poly1 = turf.polygon([[
112581          *     [-82.574787, 35.594087],
112582          *     [-82.574787, 35.615581],
112583          *     [-82.545261, 35.615581],
112584          *     [-82.545261, 35.594087],
112585          *     [-82.574787, 35.594087]
112586          * ]], {"fill": "#0f0"});
112587          * var poly2 = turf.polygon([[
112588          *     [-82.560024, 35.585153],
112589          *     [-82.560024, 35.602602],
112590          *     [-82.52964, 35.602602],
112591          *     [-82.52964, 35.585153],
112592          *     [-82.560024, 35.585153]
112593          * ]], {"fill": "#00f"});
112594          *
112595          * var union = turf.union(poly1, poly2);
112596          *
112597          * //addToMap
112598          * var addToMap = [poly1, poly2, union];
112599          */
112600         function union$1() {
112601             var arguments$1 = arguments;
112602
112603             var reader = new GeoJSONReader();
112604             var result = reader.read(JSON.stringify(arguments[0].geometry));
112605
112606             for (var i = 1; i < arguments.length; i++) {
112607                 result = UnionOp.union(result, reader.read(JSON.stringify(arguments$1[i].geometry)));
112608             }
112609
112610             var writer = new GeoJSONWriter();
112611             result = writer.write(result);
112612
112613             return {
112614                 type: 'Feature',
112615                 geometry: result,
112616                 properties: arguments[0].properties
112617             };
112618         }
112619
112620         // Reduce an array of locations into a single GeoJSON feature
112621         function _locationReducer(accumulator, location) {
112622           /* eslint-disable no-console, no-invalid-this */
112623           var result;
112624           try {
112625             var resolved = this.resolveLocation(location);
112626             if (!resolved || !resolved.feature) {
112627               console.warn(("Warning:  Couldn't resolve location \"" + location + "\""));
112628               return accumulator;
112629             }
112630             result = !accumulator ? resolved.feature : union$1(accumulator, resolved.feature);
112631           } catch (e) {
112632             console.warn(("Warning:  Error resolving location \"" + location + "\""));
112633             console.warn(e);
112634             result = accumulator;
112635           }
112636
112637           return result;
112638           /* eslint-enable no-console, no-invalid-this */
112639         }
112640
112641
112642
112643         function _cloneDeep(obj) {
112644           return JSON.parse(JSON.stringify(obj));
112645         }
112646
112647
112648         var defaultExport = function defaultExport(fc) {
112649           var this$1 = this;
112650
112651           this._cache = {};
112652
112653           // process input FeatureCollection
112654           if (fc && fc.type === 'FeatureCollection' && Array.isArray(fc.features)) {
112655             fc.features.forEach(function (feature) {
112656               feature.properties = feature.properties || {};
112657               var props = feature.properties;
112658
112659               // get `id` from either `id` or `properties`
112660               var id = feature.id || props.id;
112661               if (!id || !/^\S+\.geojson$/i.test(id)) { return; }
112662
112663               // ensure id exists and is lowercase
112664               id = id.toLowerCase();
112665               feature.id = id;
112666               props.id = id;
112667
112668               // ensure area property exists
112669               if (!props.area) {
112670                 var area = geojsonArea.geometry(feature.geometry) / 1e6;// m² to km²
112671                 props.area = Number(area.toFixed(2));
112672               }
112673
112674               this$1._cache[id] = feature;
112675             });
112676           }
112677
112678           // Replace CountryCoder world geometry to have a polygon covering the world.
112679           var world = _cloneDeep(feature('Q2'));
112680           world.geometry = {
112681             type: 'Polygon',
112682             coordinates: [[[-180, -90], [180, -90], [180, 90], [-180, 90], [-180, -90]]]
112683           };
112684           world.id = 'Q2';
112685           world.properties.id = 'Q2';
112686           world.properties.area = geojsonArea.geometry(world.geometry) / 1e6;// m² to km²
112687           this._cache.Q2 = world;
112688         };
112689
112690
112691         // validateLocation
112692         //
112693         // Pass a `location` identifier
112694         // Returns a result like
112695         // {
112696         //   type:   'point', 'geojson', or 'countrycoder'
112697         //   location:the queried location
112698         //   id:      a unique identifier
112699         // }
112700         //or `null` if the location is invalid
112701         //
112702         defaultExport.prototype.validateLocation = function validateLocation (location) {
112703           if (Array.isArray(location)) { // a [lon,lat] coordinate pair?
112704             if (location.length === 2 && Number.isFinite(location[0]) && Number.isFinite(location[1]) &&
112705               location[0] >= -180 && location[0] <= 180 && location[1] >= -90 && location[1] <= 90
112706             ) {
112707               var id = '[' + location.toString() + ']';
112708               return { type: 'point', location: location, id: id };
112709             }
112710
112711           } else if (typeof location === 'string' && /^\S+\.geojson$/i.test(location)) { // a .geojson filename?
112712             var id$1 = location.toLowerCase();
112713             if (this._cache[id$1]) {
112714               return { type: 'geojson', location: location, id: id$1 };
112715             }
112716
112717           } else if (typeof location === 'string' || typeof location === 'number') { // a country-coder value?
112718             var feature$1 = feature(location);
112719             if (feature$1) {
112720               // Use wikidata QID as the identifier, since that seems to be the only
112721               // property that everything in CountryCoder is guaranteed to have.
112722               var id$2 = feature$1.properties.wikidata;
112723               return { type: 'countrycoder', location: location, id: id$2 };
112724             }
112725           }
112726
112727           return null;
112728         };
112729
112730
112731         // resolveLocation
112732         //
112733         // Pass a `location` identifier
112734         // Returns a result like
112735         // {
112736         //   type:    'point', 'geojson', or 'countrycoder'
112737         //   location:the queried location
112738         //   id:      a unique identifier
112739         //   feature: the geojson feature
112740         // }
112741         //or `null` if the location is invalid
112742         //
112743         defaultExport.prototype.resolveLocation = function resolveLocation (location) {
112744           var valid = this.validateLocation(location);
112745           if (!valid) { return null; }
112746
112747           // return a result from cache if we can
112748           if (this._cache[valid.id]) {
112749             return Object.assign(valid, { feature: this._cache[valid.id] });
112750           }
112751
112752           // a [lon,lat] coordinate pair?
112753           if (valid.type === 'point') {
112754             var RADIUS = 25000;// meters
112755             var EDGES = 10;
112756             var PRECISION = 3;
112757             var area = Math.PI * RADIUS * RADIUS / 1e6;   // m² to km²
112758             var feature$1 = this._cache[valid.id] = geojsonPrecision({
112759               type: 'Feature',
112760               id: valid.id,
112761               properties: { id: valid.id, area: Number(area.toFixed(2)) },
112762               geometry: circleToPolygon(location, RADIUS, EDGES)
112763             }, PRECISION);
112764             return Object.assign(valid, { feature: feature$1 });
112765
112766           // a .geojson filename?
112767           } else if (valid.type === 'geojson') ; else if (valid.type === 'countrycoder') {
112768             var feature$1$1 = _cloneDeep(feature(valid.id));
112769             var props = feature$1$1.properties;
112770
112771             // -> This block of code is weird and requires some explanation. <-
112772             // CountryCoder includes higher level features which are made up of members.
112773             // These features don't have their own geometry, but CountryCoder provides an
112774             // `aggregateFeature` method to combine these members into a MultiPolygon.
112775             // BUT, when we try to actually work with these aggregated MultiPolygons,
112776             // Turf/JSTS gets crashy because of topography bugs.
112777             // SO, we'll aggregate the features ourselves by unioning them together.
112778             // This approach also has the benefit of removing all the internal boaders and
112779             // simplifying the regional polygons a lot.
112780             if (Array.isArray(props.members)) {
112781               var seed = feature$1$1.geometry ? feature$1$1 : null;
112782               var aggregate = props.members.reduce(_locationReducer.bind(this), seed);
112783               feature$1$1.geometry = aggregate.geometry;
112784             }
112785
112786             // ensure area property exists
112787             if (!props.area) {
112788               var area$1 = geojsonArea.geometry(feature$1$1.geometry) / 1e6;// m² to km²
112789               props.area = Number(area$1.toFixed(2));
112790             }
112791
112792             // ensure id property exists
112793             feature$1$1.id = valid.id;
112794             props.id = valid.id;
112795
112796             this._cache[valid.id] = feature$1$1;
112797             return Object.assign(valid, { feature: feature$1$1 });
112798           }
112799
112800           return null;
112801         };
112802
112803
112804         // resolveLocationSet
112805         //
112806         // Pass a `locationSet` Object like:
112807         // `{ include: [ Array ], exclude: [ Array ] }`
112808         // Returns a stable identifier string of the form:
112809         // "+[included]-[excluded]"
112810         //
112811         defaultExport.prototype.resolveLocationSet = function resolveLocationSet (locationSet) {
112812           locationSet = locationSet || {};
112813           var resolve = this.resolveLocation.bind(this);
112814           var include = (locationSet.include || []).map(resolve).filter(Boolean);
112815           var exclude = (locationSet.exclude || []).map(resolve).filter(Boolean);
112816
112817           if (!include.length) {
112818             include = [resolve('Q2')]; // default to 'the world'
112819           }
112820
112821           // return quickly if it's a single included location..
112822           if (include.length === 1 && exclude.length === 0) {
112823             return include[0].feature;
112824           }
112825
112826           // generate stable identifier
112827           include.sort(sortFeatures);
112828           var id = '+[' + include.map(function (d) { return d.id; }).join(',') + ']';
112829           if (exclude.length) {
112830             exclude.sort(sortFeatures);
112831             id += '-[' + exclude.map(function (d) { return d.id; }).join(',') + ']';
112832           }
112833
112834           // return cached?
112835           if (this._cache[id]) {
112836             return this._cache[id];
112837           }
112838
112839           // calculate unions
112840           var includeGeoJSON = include.map(function (d) { return d.location; }).reduce(_locationReducer.bind(this), null);
112841           var excludeGeoJSON = exclude.map(function (d) { return d.location; }).reduce(_locationReducer.bind(this), null);
112842
112843           // calculate difference, update area and return result
112844           var resultGeoJSON = excludeGeoJSON ? difference(includeGeoJSON, excludeGeoJSON) : includeGeoJSON;
112845           var area = geojsonArea.geometry(resultGeoJSON.geometry) / 1e6;// m² to km²
112846           resultGeoJSON.id = id;
112847           resultGeoJSON.properties = { id: id, area: Number(area.toFixed(2)) };
112848
112849           return this._cache[id] = resultGeoJSON;
112850
112851
112852           // Sorting the location lists is ok because they end up unioned together.
112853           // This sorting makes it possible to generate a deterministic id.
112854           function sortFeatures(a, b) {
112855             var rank = { countrycoder: 1, geojson: 2, point: 3 };
112856             var aRank = rank[a.type];
112857             var bRank = rank[b.type];
112858
112859             return (aRank > bRank) ? 1
112860               : (aRank < bRank) ? -1
112861               : a.id.localeCompare(b.id);
112862           }
112863         };
112864
112865
112866         defaultExport.prototype.cache = function cache () {
112867           return this._cache;
112868         };
112869
112870         var _oci = null;
112871
112872         function uiSuccess(context) {
112873           var MAXEVENTS = 2;
112874           var dispatch$1 = dispatch('cancel');
112875           var _changeset;
112876           var _location;
112877           ensureOSMCommunityIndex();   // start fetching the data
112878
112879
112880           function ensureOSMCommunityIndex() {
112881             var data = _mainFileFetcher;
112882             return Promise.all([ data.get('oci_resources'), data.get('oci_features') ])
112883               .then(function (vals) {
112884                 if (_oci) { return _oci; }
112885
112886                 var ociResources = vals[0].resources;
112887                 var loco = new defaultExport(vals[1]);
112888                 var ociFeatures = {};
112889
112890                 Object.values(ociResources).forEach(function (resource) {
112891                   var feature = loco.resolveLocationSet(resource.locationSet);
112892                   var ociFeature = ociFeatures[feature.id];
112893                   if (!ociFeature) {
112894                     ociFeature = JSON.parse(JSON.stringify(feature));  // deep clone
112895                     ociFeature.properties.resourceIDs = new Set();
112896                     ociFeatures[feature.id] = ociFeature;
112897                   }
112898                   ociFeature.properties.resourceIDs.add(resource.id);
112899                 });
112900
112901                 return _oci = {
112902                   features: ociFeatures,
112903                   resources: ociResources,
112904                   query: whichPolygon_1({ type: 'FeatureCollection', features: Object.values(ociFeatures) })
112905                 };
112906               });
112907           }
112908
112909
112910           // string-to-date parsing in JavaScript is weird
112911           function parseEventDate(when) {
112912             if (!when) { return; }
112913
112914             var raw = when.trim();
112915             if (!raw) { return; }
112916
112917             if (!/Z$/.test(raw)) {   // if no trailing 'Z', add one
112918               raw += 'Z';            // this forces date to be parsed as a UTC date
112919             }
112920
112921             var parsed = new Date(raw);
112922             return new Date(parsed.toUTCString().substr(0, 25));  // convert to local timezone
112923           }
112924
112925
112926           function success(selection) {
112927             var header = selection
112928               .append('div')
112929               .attr('class', 'header fillL');
112930
112931             header
112932               .append('h3')
112933               .text(_t('success.just_edited'));
112934
112935             header
112936               .append('button')
112937               .attr('class', 'close')
112938               .on('click', function () { return dispatch$1.call('cancel'); })
112939               .call(svgIcon('#iD-icon-close'));
112940
112941             var body = selection
112942               .append('div')
112943               .attr('class', 'body save-success fillL');
112944
112945             var summary = body
112946               .append('div')
112947               .attr('class', 'save-summary');
112948
112949             summary
112950               .append('h3')
112951               .text(_t('success.thank_you' + (_location ? '_location' : ''), { where: _location }));
112952
112953             summary
112954               .append('p')
112955               .text(_t('success.help_html'))
112956               .append('a')
112957               .attr('class', 'link-out')
112958               .attr('target', '_blank')
112959               .attr('tabindex', -1)
112960               .attr('href', _t('success.help_link_url'))
112961               .call(svgIcon('#iD-icon-out-link', 'inline'))
112962               .append('span')
112963               .text(_t('success.help_link_text'));
112964
112965             var osm = context.connection();
112966             if (!osm) { return; }
112967
112968             var changesetURL = osm.changesetURL(_changeset.id);
112969
112970             var table = summary
112971               .append('table')
112972               .attr('class', 'summary-table');
112973
112974             var row = table
112975               .append('tr')
112976               .attr('class', 'summary-row');
112977
112978             row
112979               .append('td')
112980               .attr('class', 'cell-icon summary-icon')
112981               .append('a')
112982               .attr('target', '_blank')
112983               .attr('href', changesetURL)
112984               .append('svg')
112985               .attr('class', 'logo-small')
112986               .append('use')
112987               .attr('xlink:href', '#iD-logo-osm');
112988
112989             var summaryDetail = row
112990               .append('td')
112991               .attr('class', 'cell-detail summary-detail');
112992
112993             summaryDetail
112994               .append('a')
112995               .attr('class', 'cell-detail summary-view-on-osm')
112996               .attr('target', '_blank')
112997               .attr('href', changesetURL)
112998               .text(_t('success.view_on_osm'));
112999
113000             summaryDetail
113001               .append('div')
113002               .html(_t('success.changeset_id', {
113003                 changeset_id: ("<a href=\"" + changesetURL + "\" target=\"_blank\">" + (_changeset.id) + "</a>")
113004               }));
113005
113006
113007             // Get OSM community index features intersecting the map..
113008             ensureOSMCommunityIndex()
113009               .then(function (oci) {
113010                 var communities = [];
113011                 var properties = oci.query(context.map().center(), true) || [];
113012
113013                 // Gather the communities from the result
113014                 properties.forEach(function (props) {
113015                   var resourceIDs = Array.from(props.resourceIDs);
113016                   resourceIDs.forEach(function (resourceID) {
113017                     var resource = oci.resources[resourceID];
113018                     communities.push({
113019                       area: props.area || Infinity,
113020                       order: resource.order || 0,
113021                       resource: resource
113022                     });
113023                   });
113024                 });
113025
113026                 // sort communities by feature area ascending, community order descending
113027                 communities.sort(function (a, b) { return a.area - b.area || b.order - a.order; });
113028
113029                 body
113030                   .call(showCommunityLinks, communities.map(function (c) { return c.resource; }));
113031               });
113032           }
113033
113034
113035           function showCommunityLinks(selection, resources) {
113036             var communityLinks = selection
113037               .append('div')
113038               .attr('class', 'save-communityLinks');
113039
113040             communityLinks
113041               .append('h3')
113042               .text(_t('success.like_osm'));
113043
113044             var table = communityLinks
113045               .append('table')
113046               .attr('class', 'community-table');
113047
113048             var row = table.selectAll('.community-row')
113049               .data(resources);
113050
113051             var rowEnter = row.enter()
113052               .append('tr')
113053               .attr('class', 'community-row');
113054
113055             rowEnter
113056               .append('td')
113057               .attr('class', 'cell-icon community-icon')
113058               .append('a')
113059               .attr('target', '_blank')
113060               .attr('href', function (d) { return d.url; })
113061               .append('svg')
113062               .attr('class', 'logo-small')
113063               .append('use')
113064               .attr('xlink:href', function (d) { return ("#community-" + (d.type)); });
113065
113066             var communityDetail = rowEnter
113067               .append('td')
113068               .attr('class', 'cell-detail community-detail');
113069
113070             communityDetail
113071               .each(showCommunityDetails);
113072
113073             communityLinks
113074               .append('div')
113075               .attr('class', 'community-missing')
113076               .text(_t('success.missing'))
113077               .append('a')
113078               .attr('class', 'link-out')
113079               .attr('target', '_blank')
113080               .attr('tabindex', -1)
113081               .call(svgIcon('#iD-icon-out-link', 'inline'))
113082               .attr('href', 'https://github.com/osmlab/osm-community-index/issues')
113083               .append('span')
113084               .text(_t('success.tell_us'));
113085           }
113086
113087
113088           function showCommunityDetails(d) {
113089             var selection = select(this);
113090             var communityID = d.id;
113091             var replacements = {
113092               url: linkify(d.url),
113093               signupUrl: linkify(d.signupUrl || d.url)
113094             };
113095
113096             selection
113097               .append('div')
113098               .attr('class', 'community-name')
113099               .append('a')
113100               .attr('target', '_blank')
113101               .attr('href', d.url)
113102               .text(_t(("community." + (d.id) + ".name")));
113103
113104             var descriptionHTML = _t(("community." + (d.id) + ".description"), replacements);
113105
113106             if (d.type === 'reddit') {   // linkify subreddits  #4997
113107               descriptionHTML = descriptionHTML
113108                 .replace(/(\/r\/\w*\/*)/i, function (match) { return linkify(d.url, match); });
113109             }
113110
113111             selection
113112               .append('div')
113113               .attr('class', 'community-description')
113114               .html(descriptionHTML);
113115
113116             if (d.extendedDescription || (d.languageCodes && d.languageCodes.length)) {
113117               selection
113118                 .append('div')
113119                 .call(uiDisclosure(context, ("community-more-" + (d.id)), false)
113120                   .expanded(false)
113121                   .updatePreference(false)
113122                   .title(_t('success.more'))
113123                   .content(showMore)
113124                 );
113125             }
113126
113127             var nextEvents = (d.events || [])
113128               .map(function (event) {
113129                 event.date = parseEventDate(event.when);
113130                 return event;
113131               })
113132               .filter(function (event) {      // date is valid and future (or today)
113133                 var t = event.date.getTime();
113134                 var now = (new Date()).setHours(0,0,0,0);
113135                 return !isNaN(t) && t >= now;
113136               })
113137               .sort(function (a, b) {       // sort by date ascending
113138                 return a.date < b.date ? -1 : a.date > b.date ? 1 : 0;
113139               })
113140               .slice(0, MAXEVENTS);   // limit number of events shown
113141
113142             if (nextEvents.length) {
113143               selection
113144                 .append('div')
113145                 .call(uiDisclosure(context, ("community-events-" + (d.id)), false)
113146                   .expanded(false)
113147                   .updatePreference(false)
113148                   .title(_t('success.events'))
113149                   .content(showNextEvents)
113150                 )
113151                 .select('.hide-toggle')
113152                 .append('span')
113153                 .attr('class', 'badge-text')
113154                 .text(nextEvents.length);
113155             }
113156
113157
113158             function showMore(selection) {
113159               var more = selection.selectAll('.community-more')
113160                 .data([0]);
113161
113162               var moreEnter = more.enter()
113163                 .append('div')
113164                 .attr('class', 'community-more');
113165
113166               if (d.extendedDescription) {
113167                 moreEnter
113168                   .append('div')
113169                   .attr('class', 'community-extended-description')
113170                   .html(_t(("community." + (d.id) + ".extendedDescription"), replacements));
113171               }
113172
113173               if (d.languageCodes && d.languageCodes.length) {
113174                 var languageList = d.languageCodes
113175                   .map(function (code) { return _mainLocalizer.languageName(code); })
113176                   .join(', ');
113177
113178                 moreEnter
113179                   .append('div')
113180                   .attr('class', 'community-languages')
113181                   .text(_t('success.languages', { languages: languageList }));
113182               }
113183             }
113184
113185
113186             function showNextEvents(selection) {
113187               var events = selection
113188                 .append('div')
113189                 .attr('class', 'community-events');
113190
113191               var item = events.selectAll('.community-event')
113192                 .data(nextEvents);
113193
113194               var itemEnter = item.enter()
113195                 .append('div')
113196                 .attr('class', 'community-event');
113197
113198               itemEnter
113199                 .append('div')
113200                 .attr('class', 'community-event-name')
113201                 .append('a')
113202                 .attr('target', '_blank')
113203                 .attr('href', function (d) { return d.url; })
113204                 .text(function (d) {
113205                   var name = d.name;
113206                   if (d.i18n && d.id) {
113207                     name = _t(("community." + communityID + ".events." + (d.id) + ".name"), { default: name });
113208                   }
113209                   return name;
113210                 });
113211
113212               itemEnter
113213                 .append('div')
113214                 .attr('class', 'community-event-when')
113215                 .text(function (d) {
113216                   var options = { weekday: 'short', day: 'numeric', month: 'short', year: 'numeric' };
113217                   if (d.date.getHours() || d.date.getMinutes()) {   // include time if it has one
113218                     options.hour = 'numeric';
113219                     options.minute = 'numeric';
113220                   }
113221                   return d.date.toLocaleString(_mainLocalizer.localeCode(), options);
113222                 });
113223
113224               itemEnter
113225                 .append('div')
113226                 .attr('class', 'community-event-where')
113227                 .text(function (d) {
113228                   var where = d.where;
113229                   if (d.i18n && d.id) {
113230                     where = _t(("community." + communityID + ".events." + (d.id) + ".where"), { default: where });
113231                   }
113232                   return where;
113233                 });
113234
113235               itemEnter
113236                 .append('div')
113237                 .attr('class', 'community-event-description')
113238                 .text(function (d) {
113239                   var description = d.description;
113240                   if (d.i18n && d.id) {
113241                     description = _t(("community." + communityID + ".events." + (d.id) + ".description"), { default: description });
113242                   }
113243                   return description;
113244                 });
113245             }
113246
113247
113248             function linkify(url, text) {
113249               text = text || url;
113250               return ("<a target=\"_blank\" href=\"" + url + "\">" + text + "</a>");
113251             }
113252           }
113253
113254
113255           success.changeset = function(val) {
113256             if (!arguments.length) { return _changeset; }
113257             _changeset = val;
113258             return success;
113259           };
113260
113261
113262           success.location = function(val) {
113263             if (!arguments.length) { return _location; }
113264             _location = val;
113265             return success;
113266           };
113267
113268
113269           return utilRebind(success, dispatch$1, 'on');
113270         }
113271
113272         function modeSave(context) {
113273             var mode = { id: 'save' };
113274             var keybinding = utilKeybinding('modeSave');
113275
113276             var commit = uiCommit(context)
113277                 .on('cancel', cancel);
113278             var _conflictsUi; // uiConflicts
113279
113280             var _location;
113281             var _success;
113282
113283             var uploader = context.uploader()
113284                 .on('saveStarted.modeSave', function() {
113285                     keybindingOff();
113286                 })
113287                 // fire off some async work that we want to be ready later
113288                 .on('willAttemptUpload.modeSave', prepareForSuccess)
113289                 .on('progressChanged.modeSave', showProgress)
113290                 .on('resultNoChanges.modeSave', function() {
113291                     cancel();
113292                 })
113293                 .on('resultErrors.modeSave', showErrors)
113294                 .on('resultConflicts.modeSave', showConflicts)
113295                 .on('resultSuccess.modeSave', showSuccess);
113296
113297
113298             function cancel() {
113299                 context.enter(modeBrowse(context));
113300             }
113301
113302
113303             function showProgress(num, total) {
113304                 var modal = context.container().select('.loading-modal .modal-section');
113305                 var progress = modal.selectAll('.progress')
113306                     .data([0]);
113307
113308                 // enter/update
113309                 progress.enter()
113310                     .append('div')
113311                     .attr('class', 'progress')
113312                     .merge(progress)
113313                     .text(_t('save.conflict_progress', { num: num, total: total }));
113314             }
113315
113316
113317             function showConflicts(changeset, conflicts, origChanges) {
113318
113319                 var selection = context.container()
113320                     .select('.sidebar')
113321                     .append('div')
113322                     .attr('class','sidebar-component');
113323
113324                 context.container().selectAll('.main-content')
113325                     .classed('active', true)
113326                     .classed('inactive', false);
113327
113328                 _conflictsUi = uiConflicts(context)
113329                     .conflictList(conflicts)
113330                     .origChanges(origChanges)
113331                     .on('cancel', function() {
113332                         context.container().selectAll('.main-content')
113333                             .classed('active', false)
113334                             .classed('inactive', true);
113335                         selection.remove();
113336                         keybindingOn();
113337
113338                         uploader.cancelConflictResolution();
113339                     })
113340                     .on('save', function() {
113341                         context.container().selectAll('.main-content')
113342                             .classed('active', false)
113343                             .classed('inactive', true);
113344                         selection.remove();
113345
113346                         uploader.processResolvedConflicts(changeset);
113347                     });
113348
113349                 selection.call(_conflictsUi);
113350             }
113351
113352
113353             function showErrors(errors) {
113354                 keybindingOn();
113355
113356                 var selection = uiConfirm(context.container());
113357                 selection
113358                     .select('.modal-section.header')
113359                     .append('h3')
113360                     .text(_t('save.error'));
113361
113362                 addErrors(selection, errors);
113363                 selection.okButton();
113364             }
113365
113366
113367             function addErrors(selection, data) {
113368                 var message = selection
113369                     .select('.modal-section.message-text');
113370
113371                 var items = message
113372                     .selectAll('.error-container')
113373                     .data(data);
113374
113375                 var enter = items.enter()
113376                     .append('div')
113377                     .attr('class', 'error-container');
113378
113379                 enter
113380                     .append('a')
113381                     .attr('class', 'error-description')
113382                     .attr('href', '#')
113383                     .classed('hide-toggle', true)
113384                     .text(function(d) { return d.msg || _t('save.unknown_error_details'); })
113385                     .on('click', function() {
113386                         event.preventDefault();
113387
113388                         var error = select(this);
113389                         var detail = select(this.nextElementSibling);
113390                         var exp = error.classed('expanded');
113391
113392                         detail.style('display', exp ? 'none' : 'block');
113393                         error.classed('expanded', !exp);
113394                     });
113395
113396                 var details = enter
113397                     .append('div')
113398                     .attr('class', 'error-detail-container')
113399                     .style('display', 'none');
113400
113401                 details
113402                     .append('ul')
113403                     .attr('class', 'error-detail-list')
113404                     .selectAll('li')
113405                     .data(function(d) { return d.details || []; })
113406                     .enter()
113407                     .append('li')
113408                     .attr('class', 'error-detail-item')
113409                     .text(function(d) { return d; });
113410
113411                 items.exit()
113412                     .remove();
113413             }
113414
113415
113416             function showSuccess(changeset) {
113417                 commit.reset();
113418
113419                 var ui = _success
113420                     .changeset(changeset)
113421                     .location(_location)
113422                     .on('cancel', function() { context.ui().sidebar.hide(); });
113423
113424                 context.enter(modeBrowse(context).sidebar(ui));
113425             }
113426
113427
113428             function keybindingOn() {
113429                 select(document)
113430                     .call(keybinding.on('⎋', cancel, true));
113431             }
113432
113433
113434             function keybindingOff() {
113435                 select(document)
113436                     .call(keybinding.unbind);
113437             }
113438
113439
113440             // Reverse geocode current map location so we can display a message on
113441             // the success screen like "Thank you for editing around place, region."
113442             function prepareForSuccess() {
113443                 _success = uiSuccess(context);
113444                 _location = null;
113445                 if (!services.geocoder) { return; }
113446
113447                 services.geocoder.reverse(context.map().center(), function(err, result) {
113448                     if (err || !result || !result.address) { return; }
113449
113450                     var addr = result.address;
113451                     var place = (addr && (addr.town || addr.city || addr.county)) || '';
113452                     var region = (addr && (addr.state || addr.country)) || '';
113453                     var separator = (place && region) ? _t('success.thank_you_where.separator') : '';
113454
113455                     _location = _t('success.thank_you_where.format',
113456                         { place: place, separator: separator, region: region }
113457                     );
113458                 });
113459             }
113460
113461
113462             mode.selectedIDs = function() {
113463                 return _conflictsUi ? _conflictsUi.shownEntityIds() : [];
113464             };
113465
113466
113467             mode.enter = function() {
113468                 // Show sidebar
113469                 context.ui().sidebar.expand();
113470
113471                 function done() {
113472                     context.ui().sidebar.show(commit);
113473                 }
113474
113475                 keybindingOn();
113476
113477                 context.container().selectAll('.main-content')
113478                     .classed('active', false)
113479                     .classed('inactive', true);
113480
113481                 var osm = context.connection();
113482                 if (!osm) {
113483                     cancel();
113484                     return;
113485                 }
113486
113487                 if (osm.authenticated()) {
113488                     done();
113489                 } else {
113490                     osm.authenticate(function(err) {
113491                         if (err) {
113492                             cancel();
113493                         } else {
113494                             done();
113495                         }
113496                     });
113497                 }
113498             };
113499
113500
113501             mode.exit = function() {
113502
113503                 keybindingOff();
113504
113505                 context.container().selectAll('.main-content')
113506                     .classed('active', true)
113507                     .classed('inactive', false);
113508
113509                 context.ui().sidebar.hide();
113510             };
113511
113512             return mode;
113513         }
113514
113515         function uiToolOldDrawModes(context) {
113516
113517             var tool = {
113518                 id: 'old_modes',
113519                 label: _t('toolbar.add_feature')
113520             };
113521
113522             var modes = [
113523                 modeAddPoint(context, {
113524                     title: _t('modes.add_point.title'),
113525                     button: 'point',
113526                     description: _t('modes.add_point.description'),
113527                     preset: _mainPresetIndex.item('point'),
113528                     key: '1'
113529                 }),
113530                 modeAddLine(context, {
113531                     title: _t('modes.add_line.title'),
113532                     button: 'line',
113533                     description: _t('modes.add_line.description'),
113534                     preset: _mainPresetIndex.item('line'),
113535                     key: '2'
113536                 }),
113537                 modeAddArea(context, {
113538                     title: _t('modes.add_area.title'),
113539                     button: 'area',
113540                     description: _t('modes.add_area.description'),
113541                     preset: _mainPresetIndex.item('area'),
113542                     key: '3'
113543                 })
113544             ];
113545
113546
113547             function enabled() {
113548                 return osmEditable();
113549             }
113550
113551             function osmEditable() {
113552                 return context.editable();
113553             }
113554
113555             modes.forEach(function(mode) {
113556                 context.keybinding().on(mode.key, function() {
113557                     if (!enabled()) { return; }
113558
113559                     if (mode.id === context.mode().id) {
113560                         context.enter(modeBrowse(context));
113561                     } else {
113562                         context.enter(mode);
113563                     }
113564                 });
113565             });
113566
113567             tool.render = function(selection) {
113568
113569                 var wrap = selection
113570                     .append('div')
113571                     .attr('class', 'joined')
113572                     .style('display', 'flex');
113573
113574                 var debouncedUpdate = debounce(update, 500, { leading: true, trailing: true });
113575
113576                 context.map()
113577                     .on('move.modes', debouncedUpdate)
113578                     .on('drawn.modes', debouncedUpdate);
113579
113580                 context
113581                     .on('enter.modes', update);
113582
113583                 update();
113584
113585
113586                 function update() {
113587
113588                     var buttons = wrap.selectAll('button.add-button')
113589                         .data(modes, function(d) { return d.id; });
113590
113591                     // exit
113592                     buttons.exit()
113593                         .remove();
113594
113595                     // enter
113596                     var buttonsEnter = buttons.enter()
113597                         .append('button')
113598                         .attr('class', function(d) { return d.id + ' add-button bar-button'; })
113599                         .on('click.mode-buttons', function(d) {
113600                             if (!enabled()) { return; }
113601
113602                             // When drawing, ignore accidental clicks on mode buttons - #4042
113603                             var currMode = context.mode().id;
113604                             if (/^draw/.test(currMode)) { return; }
113605
113606                             if (d.id === currMode) {
113607                                 context.enter(modeBrowse(context));
113608                             } else {
113609                                 context.enter(d);
113610                             }
113611                         })
113612                         .call(uiTooltip()
113613                             .placement('bottom')
113614                             .title(function(d) { return d.description; })
113615                             .keys(function(d) { return [d.key]; })
113616                             .scrollContainer(context.container().select('.top-toolbar'))
113617                         );
113618
113619                     buttonsEnter
113620                         .each(function(d) {
113621                             select(this)
113622                                 .call(svgIcon('#iD-icon-' + d.button));
113623                         });
113624
113625                     buttonsEnter
113626                         .append('span')
113627                         .attr('class', 'label')
113628                         .text(function(mode) { return mode.title; });
113629
113630                     // if we are adding/removing the buttons, check if toolbar has overflowed
113631                     if (buttons.enter().size() || buttons.exit().size()) {
113632                         context.ui().checkOverflow('.top-toolbar', true);
113633                     }
113634
113635                     // update
113636                     buttons = buttons
113637                         .merge(buttonsEnter)
113638                         .classed('disabled', function(d) { return !enabled(); })
113639                         .classed('active', function(d) { return context.mode() && context.mode().button === d.button; });
113640                 }
113641             };
113642
113643             return tool;
113644         }
113645
113646         function uiToolNotes(context) {
113647
113648             var tool = {
113649                 id: 'notes',
113650                 label: _t('modes.add_note.label')
113651             };
113652
113653             var mode = modeAddNote(context);
113654
113655             function enabled() {
113656                 return notesEnabled() && notesEditable();
113657             }
113658
113659             function notesEnabled() {
113660                 var noteLayer = context.layers().layer('notes');
113661                 return noteLayer && noteLayer.enabled();
113662             }
113663
113664             function notesEditable() {
113665                 var mode = context.mode();
113666                 return context.map().notesEditable() && mode && mode.id !== 'save';
113667             }
113668
113669             context.keybinding().on(mode.key, function() {
113670                 if (!enabled()) { return; }
113671
113672                 if (mode.id === context.mode().id) {
113673                     context.enter(modeBrowse(context));
113674                 } else {
113675                     context.enter(mode);
113676                 }
113677             });
113678
113679             tool.render = function(selection) {
113680
113681
113682                 var debouncedUpdate = debounce(update, 500, { leading: true, trailing: true });
113683
113684                 context.map()
113685                     .on('move.notes', debouncedUpdate)
113686                     .on('drawn.notes', debouncedUpdate);
113687
113688                 context
113689                     .on('enter.notes', update);
113690
113691                 update();
113692
113693
113694                 function update() {
113695                     var showNotes = notesEnabled();
113696                     var data = showNotes ? [mode] : [];
113697
113698                     var buttons = selection.selectAll('button.add-button')
113699                         .data(data, function(d) { return d.id; });
113700
113701                     // exit
113702                     buttons.exit()
113703                         .remove();
113704
113705                     // enter
113706                     var buttonsEnter = buttons.enter()
113707                         .append('button')
113708                         .attr('tabindex', -1)
113709                         .attr('class', function(d) { return d.id + ' add-button bar-button'; })
113710                         .on('click.notes', function(d) {
113711                             if (!enabled()) { return; }
113712
113713                             // When drawing, ignore accidental clicks on mode buttons - #4042
113714                             var currMode = context.mode().id;
113715                             if (/^draw/.test(currMode)) { return; }
113716
113717                             if (d.id === currMode) {
113718                                 context.enter(modeBrowse(context));
113719                             } else {
113720                                 context.enter(d);
113721                             }
113722                         })
113723                         .call(uiTooltip()
113724                             .placement('bottom')
113725                             .title(function(d) { return d.description; })
113726                             .keys(function(d) { return [d.key]; })
113727                             .scrollContainer(context.container().select('.top-toolbar'))
113728                         );
113729
113730                     buttonsEnter
113731                         .each(function(d) {
113732                             select(this)
113733                                 .call(svgIcon(d.icon || '#iD-icon-' + d.button));
113734                         });
113735
113736                     // if we are adding/removing the buttons, check if toolbar has overflowed
113737                     if (buttons.enter().size() || buttons.exit().size()) {
113738                         context.ui().checkOverflow('.top-toolbar', true);
113739                     }
113740
113741                     // update
113742                     buttons = buttons
113743                         .merge(buttonsEnter)
113744                         .classed('disabled', function(d) { return !enabled(); })
113745                         .classed('active', function(d) { return context.mode() && context.mode().button === d.button; });
113746                 }
113747             };
113748
113749             tool.uninstall = function() {
113750                 context
113751                     .on('enter.editor.notes', null)
113752                     .on('exit.editor.notes', null)
113753                     .on('enter.notes', null);
113754
113755                 context.map()
113756                     .on('move.notes', null)
113757                     .on('drawn.notes', null);
113758             };
113759
113760             return tool;
113761         }
113762
113763         function uiToolSave(context) {
113764
113765             var tool = {
113766                 id: 'save',
113767                 label: _t('save.title')
113768             };
113769
113770             var button = null;
113771             var tooltipBehavior = null;
113772             var history = context.history();
113773             var key = uiCmd('⌘S');
113774             var _numChanges = 0;
113775
113776             function isSaving() {
113777                 var mode = context.mode();
113778                 return mode && mode.id === 'save';
113779             }
113780
113781             function isDisabled() {
113782                 return _numChanges === 0 || isSaving();
113783             }
113784
113785             function save() {
113786                 event.preventDefault();
113787                 if (!context.inIntro() && !isSaving() && history.hasChanges()) {
113788                     context.enter(modeSave(context));
113789                 }
113790             }
113791
113792             function bgColor() {
113793                 var step;
113794                 if (_numChanges === 0) {
113795                     return null;
113796                 } else if (_numChanges <= 50) {
113797                     step = _numChanges / 50;
113798                     return d3_interpolateRgb('#fff', '#ff8')(step);  // white -> yellow
113799                 } else {
113800                     step = Math.min((_numChanges - 50) / 50, 1.0);
113801                     return d3_interpolateRgb('#ff8', '#f88')(step);  // yellow -> red
113802                 }
113803             }
113804
113805             function updateCount() {
113806                 var val = history.difference().summary().length;
113807                 if (val === _numChanges) { return; }
113808
113809                 _numChanges = val;
113810
113811                 if (tooltipBehavior) {
113812                     tooltipBehavior
113813                         .title(_t(_numChanges > 0 ? 'save.help' : 'save.no_changes'))
113814                         .keys([key]);
113815                 }
113816
113817                 if (button) {
113818                     button
113819                         .classed('disabled', isDisabled())
113820                         .style('background', bgColor());
113821
113822                     button.select('span.count')
113823                         .text(_numChanges);
113824                 }
113825             }
113826
113827
113828             tool.render = function(selection) {
113829                 tooltipBehavior = uiTooltip()
113830                     .placement('bottom')
113831                     .title(_t('save.no_changes'))
113832                     .keys([key])
113833                     .scrollContainer(context.container().select('.top-toolbar'));
113834
113835                 var lastPointerUpType;
113836
113837                 button = selection
113838                     .append('button')
113839                     .attr('class', 'save disabled bar-button')
113840                     .on('pointerup', function() {
113841                         lastPointerUpType = event.pointerType;
113842                     })
113843                     .on('click', function() {
113844                         event.preventDefault();
113845
113846                         save();
113847
113848                         if (_numChanges === 0 && (
113849                             lastPointerUpType === 'touch' ||
113850                             lastPointerUpType === 'pen')
113851                         ) {
113852                             // there are no tooltips for touch interactions so flash feedback instead
113853                             context.ui().flash
113854                                 .duration(2000)
113855                                 .iconName('#iD-icon-save')
113856                                 .iconClass('disabled')
113857                                 .text(_t('save.no_changes'))();
113858                         }
113859                         lastPointerUpType = null;
113860                     })
113861                     .call(tooltipBehavior);
113862
113863                 button
113864                     .call(svgIcon('#iD-icon-save'));
113865
113866                 button
113867                     .append('span')
113868                     .attr('class', 'count')
113869                     .attr('aria-hidden', 'true')
113870                     .text('0');
113871
113872                 updateCount();
113873
113874
113875                 context.keybinding()
113876                     .on(key, save, true);
113877
113878
113879                 context.history()
113880                     .on('change.save', updateCount);
113881
113882                 context
113883                     .on('enter.save', function() {
113884                         if (button) {
113885                             button
113886                                 .classed('disabled', isDisabled());
113887
113888                             if (isSaving()) {
113889                                 button.call(tooltipBehavior.hide);
113890                             }
113891                         }
113892                     });
113893             };
113894
113895
113896             tool.uninstall = function() {
113897                 context.keybinding()
113898                     .off(key, true);
113899
113900                 context.history()
113901                     .on('change.save', null);
113902
113903                 context
113904                     .on('enter.save', null);
113905
113906                 button = null;
113907                 tooltipBehavior = null;
113908             };
113909
113910             return tool;
113911         }
113912
113913         function uiToolSidebarToggle(context) {
113914
113915             var tool = {
113916                 id: 'sidebar_toggle',
113917                 label: _t('toolbar.inspect')
113918             };
113919
113920             tool.render = function(selection) {
113921                 selection
113922                     .append('button')
113923                     .attr('class', 'bar-button')
113924                     .on('click', function() {
113925                         context.ui().sidebar.toggle();
113926                     })
113927                     .call(uiTooltip()
113928                         .placement('bottom')
113929                         .title(_t('sidebar.tooltip'))
113930                         .keys([_t('sidebar.key')])
113931                         .scrollContainer(context.container().select('.top-toolbar'))
113932                     )
113933                     .call(svgIcon('#iD-icon-sidebar-' + (_mainLocalizer.textDirection() === 'rtl' ? 'right' : 'left')));
113934             };
113935
113936             return tool;
113937         }
113938
113939         function uiToolUndoRedo(context) {
113940
113941             var tool = {
113942                 id: 'undo_redo',
113943                 label: _t('toolbar.undo_redo')
113944             };
113945
113946             var commands = [{
113947                 id: 'undo',
113948                 cmd: uiCmd('⌘Z'),
113949                 action: function() {
113950                     context.undo();
113951                 },
113952                 annotation: function() {
113953                     return context.history().undoAnnotation();
113954                 },
113955                 icon: 'iD-icon-' + (_mainLocalizer.textDirection() === 'rtl' ? 'redo' : 'undo')
113956             }, {
113957                 id: 'redo',
113958                 cmd: uiCmd('⌘⇧Z'),
113959                 action: function() {
113960                     context.redo();
113961                 },
113962                 annotation: function() {
113963                     return context.history().redoAnnotation();
113964                 },
113965                 icon: 'iD-icon-' + (_mainLocalizer.textDirection() === 'rtl' ? 'undo' : 'redo')
113966             }];
113967
113968
113969             function editable() {
113970                 return context.mode() && context.mode().id !== 'save' && context.map().editableDataEnabled(true /* ignore min zoom */);
113971             }
113972
113973
113974             tool.render = function(selection) {
113975                 var tooltipBehavior = uiTooltip()
113976                     .placement('bottom')
113977                     .title(function (d) {
113978                         return d.annotation() ?
113979                             _t(d.id + '.tooltip', { action: d.annotation() }) :
113980                             _t(d.id + '.nothing');
113981                     })
113982                     .keys(function(d) {
113983                         return [d.cmd];
113984                     })
113985                     .scrollContainer(context.container().select('.top-toolbar'));
113986
113987                 var lastPointerUpType;
113988
113989                 var buttons = selection.selectAll('button')
113990                     .data(commands)
113991                     .enter()
113992                     .append('button')
113993                     .attr('class', function(d) { return 'disabled ' + d.id + '-button bar-button'; })
113994                     .on('pointerup', function() {
113995                         // `pointerup` is always called before `click`
113996                         lastPointerUpType = event.pointerType;
113997                     })
113998                     .on('click', function(d) {
113999                         event.preventDefault();
114000
114001                         var annotation = d.annotation();
114002
114003                         if (editable() && annotation) {
114004                             d.action();
114005                         }
114006
114007                         if (editable() && (
114008                             lastPointerUpType === 'touch' ||
114009                             lastPointerUpType === 'pen')
114010                         ) {
114011                             // there are no tooltips for touch interactions so flash feedback instead
114012
114013                             var text = annotation ?
114014                                 _t(d.id + '.tooltip', { action: annotation }) :
114015                                 _t(d.id + '.nothing');
114016                             context.ui().flash
114017                                 .duration(2000)
114018                                 .iconName('#' + d.icon)
114019                                 .iconClass(annotation ? '' : 'disabled')
114020                                 .text(text)();
114021                         }
114022                         lastPointerUpType = null;
114023                     })
114024                     .call(tooltipBehavior);
114025
114026                 buttons.each(function(d) {
114027                     select(this)
114028                         .call(svgIcon('#' + d.icon));
114029                 });
114030
114031                 context.keybinding()
114032                     .on(commands[0].cmd, function() {
114033                         event.preventDefault();
114034                         if (editable()) { commands[0].action(); }
114035                     })
114036                     .on(commands[1].cmd, function() {
114037                         event.preventDefault();
114038                         if (editable()) { commands[1].action(); }
114039                     });
114040
114041
114042                 var debouncedUpdate = debounce(update, 500, { leading: true, trailing: true });
114043
114044                 context.map()
114045                     .on('move.undo_redo', debouncedUpdate)
114046                     .on('drawn.undo_redo', debouncedUpdate);
114047
114048                 context.history()
114049                     .on('change.undo_redo', function(difference) {
114050                         if (difference) { update(); }
114051                     });
114052
114053                 context
114054                     .on('enter.undo_redo', update);
114055
114056
114057                 function update() {
114058                     buttons
114059                         .classed('disabled', function(d) {
114060                             return !editable() || !d.annotation();
114061                         })
114062                         .each(function() {
114063                             var selection = select(this);
114064                             if (!selection.select('.tooltip.in').empty()) {
114065                                 selection.call(tooltipBehavior.updateContent);
114066                             }
114067                         });
114068                 }
114069             };
114070
114071             tool.uninstall = function() {
114072                 context.keybinding()
114073                     .off(commands[0].cmd)
114074                     .off(commands[1].cmd);
114075
114076                 context.map()
114077                     .on('move.undo_redo', null)
114078                     .on('drawn.undo_redo', null);
114079
114080                 context.history()
114081                     .on('change.undo_redo', null);
114082
114083                 context
114084                     .on('enter.undo_redo', null);
114085             };
114086
114087             return tool;
114088         }
114089
114090         function uiTopToolbar(context) {
114091
114092             var sidebarToggle = uiToolSidebarToggle(context),
114093                 modes = uiToolOldDrawModes(context),
114094                 notes = uiToolNotes(context),
114095                 undoRedo = uiToolUndoRedo(context),
114096                 save = uiToolSave(context);
114097
114098             function notesEnabled() {
114099                 var noteLayer = context.layers().layer('notes');
114100                 return noteLayer && noteLayer.enabled();
114101             }
114102
114103             function topToolbar(bar) {
114104
114105                 bar.on('wheel.topToolbar', function() {
114106                     if (!event.deltaX) {
114107                         // translate vertical scrolling into horizontal scrolling in case
114108                         // the user doesn't have an input device that can scroll horizontally
114109                         bar.node().scrollLeft += event.deltaY;
114110                     }
114111                 });
114112
114113                 var debouncedUpdate = debounce(update, 500, { leading: true, trailing: true });
114114                 context.layers()
114115                     .on('change.topToolbar', debouncedUpdate);
114116
114117                 update();
114118
114119                 function update() {
114120
114121                     var tools = [
114122                         sidebarToggle,
114123                         'spacer',
114124                         modes
114125                     ];
114126
114127                     tools.push('spacer');
114128
114129                     if (notesEnabled()) {
114130                         tools = tools.concat([notes, 'spacer']);
114131                     }
114132
114133                     tools = tools.concat([undoRedo, save]);
114134
114135                     var toolbarItems = bar.selectAll('.toolbar-item')
114136                         .data(tools, function(d) {
114137                             return d.id || d;
114138                         });
114139
114140                     toolbarItems.exit()
114141                         .each(function(d) {
114142                             if (d.uninstall) {
114143                                 d.uninstall();
114144                             }
114145                         })
114146                         .remove();
114147
114148                     var itemsEnter = toolbarItems
114149                         .enter()
114150                         .append('div')
114151                         .attr('class', function(d) {
114152                             var classes = 'toolbar-item ' + (d.id || d).replace('_', '-');
114153                             if (d.klass) { classes += ' ' + d.klass; }
114154                             return classes;
114155                         });
114156
114157                     var actionableItems = itemsEnter.filter(function(d) { return d !== 'spacer'; });
114158
114159                     actionableItems
114160                         .append('div')
114161                         .attr('class', 'item-content')
114162                         .each(function(d) {
114163                             select(this).call(d.render, bar);
114164                         });
114165
114166                     actionableItems
114167                         .append('div')
114168                         .attr('class', 'item-label')
114169                         .text(function(d) {
114170                             return d.label;
114171                         });
114172                 }
114173
114174             }
114175
114176             return topToolbar;
114177         }
114178
114179         // these are module variables so they are preserved through a ui.restart()
114180         var sawVersion = null;
114181         var isNewVersion = false;
114182         var isNewUser = false;
114183
114184
114185         function uiVersion(context) {
114186
114187             var currVersion = context.version;
114188             var matchedVersion = currVersion.match(/\d+\.\d+\.\d+.*/);
114189
114190             if (sawVersion === null && matchedVersion !== null) {
114191                 if (corePreferences('sawVersion')) {
114192                     isNewUser = false;
114193                     isNewVersion = corePreferences('sawVersion') !== currVersion && currVersion.indexOf('-') === -1;
114194                 } else {
114195                     isNewUser = true;
114196                     isNewVersion = true;
114197                 }
114198                 corePreferences('sawVersion', currVersion);
114199                 sawVersion = currVersion;
114200             }
114201
114202             return function(selection) {
114203                 selection
114204                     .append('a')
114205                     .attr('target', '_blank')
114206                     .attr('href', 'https://github.com/openstreetmap/iD')
114207                     .text(currVersion);
114208
114209                 // only show new version indicator to users that have used iD before
114210                 if (isNewVersion && !isNewUser) {
114211                     selection
114212                         .append('div')
114213                         .attr('class', 'badge')
114214                         .append('a')
114215                         .attr('target', '_blank')
114216                         .attr('href', 'https://github.com/openstreetmap/iD/blob/release/CHANGELOG.md#whats-new')
114217                         .call(svgIcon('#maki-gift-11'))
114218                         .call(uiTooltip()
114219                             .title(_t('version.whats_new', { version: currVersion }))
114220                             .placement('top')
114221                         );
114222                 }
114223             };
114224         }
114225
114226         function uiZoom(context) {
114227
114228             var zooms = [{
114229                 id: 'zoom-in',
114230                 icon: 'iD-icon-plus',
114231                 title: _t('zoom.in'),
114232                 action: zoomIn,
114233                 disabled: function() {
114234                     return !context.map().canZoomIn();
114235                 },
114236                 disabledTitle: _t('zoom.disabled.in'),
114237                 key: '+'
114238             }, {
114239                 id: 'zoom-out',
114240                 icon: 'iD-icon-minus',
114241                 title: _t('zoom.out'),
114242                 action: zoomOut,
114243                 disabled: function() {
114244                     return !context.map().canZoomOut();
114245                 },
114246                 disabledTitle: _t('zoom.disabled.out'),
114247                 key: '-'
114248             }];
114249
114250             function zoomIn() {
114251                 event.preventDefault();
114252                 context.map().zoomIn();
114253             }
114254
114255             function zoomOut() {
114256                 event.preventDefault();
114257                 context.map().zoomOut();
114258             }
114259
114260             function zoomInFurther() {
114261                 event.preventDefault();
114262                 context.map().zoomInFurther();
114263             }
114264
114265             function zoomOutFurther() {
114266                 event.preventDefault();
114267                 context.map().zoomOutFurther();
114268             }
114269
114270             return function(selection) {
114271                 var tooltipBehavior = uiTooltip()
114272                     .placement((_mainLocalizer.textDirection() === 'rtl') ? 'right' : 'left')
114273                     .title(function(d) {
114274                         if (d.disabled()) {
114275                             return d.disabledTitle;
114276                         }
114277                         return d.title;
114278                     })
114279                     .keys(function(d) {
114280                         return [d.key];
114281                     });
114282
114283                 var lastPointerUpType;
114284
114285                 var buttons = selection.selectAll('button')
114286                     .data(zooms)
114287                     .enter()
114288                     .append('button')
114289                     .attr('class', function(d) { return d.id; })
114290                     .on('pointerup.editor', function() {
114291                         lastPointerUpType = event.pointerType;
114292                     })
114293                     .on('click.editor', function(d) {
114294                         if (!d.disabled()) {
114295                             d.action();
114296                         } else if (lastPointerUpType === 'touch' || lastPointerUpType === 'pen') {
114297                             context.ui().flash
114298                                 .duration(2000)
114299                                 .iconName('#' + d.icon)
114300                                 .iconClass('disabled')
114301                                 .text(d.disabledTitle)();
114302                         }
114303                         lastPointerUpType = null;
114304                     })
114305                     .call(tooltipBehavior);
114306
114307                 buttons.each(function(d) {
114308                     select(this)
114309                         .call(svgIcon('#' + d.icon, 'light'));
114310                 });
114311
114312                 ['plus', 'ffplus', '=', 'ffequals'].forEach(function(key) {
114313                     context.keybinding().on([key], zoomIn);
114314                     context.keybinding().on([uiCmd('⌘' + key)], zoomInFurther);
114315                 });
114316
114317                 ['_', '-', 'ffminus', 'dash'].forEach(function(key) {
114318                     context.keybinding().on([key], zoomOut);
114319                     context.keybinding().on([uiCmd('⌘' + key)], zoomOutFurther);
114320                 });
114321
114322                 function updateButtonStates() {
114323                     buttons
114324                         .classed('disabled', function(d) {
114325                             return d.disabled();
114326                         })
114327                         .each(function() {
114328                             var selection = select(this);
114329                             if (!selection.select('.tooltip.in').empty()) {
114330                                 selection.call(tooltipBehavior.updateContent);
114331                             }
114332                         });
114333                 }
114334
114335                 updateButtonStates();
114336
114337                 context.map().on('move.uiZoom', updateButtonStates);
114338             };
114339         }
114340
114341         function uiZoomToSelection(context) {
114342
114343             function isDisabled() {
114344                 var mode = context.mode();
114345                 return !mode || !mode.zoomToSelected;
114346             }
114347
114348             var _lastPointerUpType;
114349
114350             function pointerup() {
114351                 _lastPointerUpType = event.pointerType;
114352             }
114353
114354             function click() {
114355                 event.preventDefault();
114356
114357                 if (isDisabled()) {
114358                     if (_lastPointerUpType === 'touch' || _lastPointerUpType === 'pen') {
114359                         context.ui().flash
114360                             .duration(2000)
114361                             .iconName('#iD-icon-framed-dot')
114362                             .iconClass('disabled')
114363                             .text(_t('inspector.zoom_to.no_selection'))();
114364                     }
114365                 } else {
114366                     var mode = context.mode();
114367                     if (mode && mode.zoomToSelected) {
114368                         mode.zoomToSelected();
114369                     }
114370                 }
114371
114372                 _lastPointerUpType = null;
114373             }
114374
114375             return function(selection) {
114376
114377                 var tooltipBehavior = uiTooltip()
114378                     .placement((_mainLocalizer.textDirection() === 'rtl') ? 'right' : 'left')
114379                     .title(function() {
114380                         if (isDisabled()) {
114381                             return _t('inspector.zoom_to.no_selection');
114382                         }
114383                         return _t('inspector.zoom_to.title');
114384                     })
114385                     .keys([_t('inspector.zoom_to.key')]);
114386
114387                 var button = selection
114388                     .append('button')
114389                     .on('pointerup', pointerup)
114390                     .on('click', click)
114391                     .call(svgIcon('#iD-icon-framed-dot', 'light'))
114392                     .call(tooltipBehavior);
114393
114394                 function setEnabledState() {
114395                     button.classed('disabled', isDisabled());
114396                     if (!button.select('.tooltip.in').empty()) {
114397                         button.call(tooltipBehavior.updateContent);
114398                     }
114399                 }
114400
114401                 context.on('enter.uiZoomToSelection', setEnabledState);
114402
114403                 setEnabledState();
114404             };
114405         }
114406
114407         function uiPane(id, context) {
114408
114409             var _key;
114410             var _title = '';
114411             var _description = '';
114412             var _iconName = '';
114413             var _sections; // array of uiSection objects
114414
114415             var _paneSelection = select(null);
114416
114417             var _paneTooltip;
114418
114419             var pane = {
114420                 id: id
114421             };
114422
114423             pane.title = function(val) {
114424                 if (!arguments.length) { return _title; }
114425                 _title = val;
114426                 return pane;
114427             };
114428
114429             pane.key = function(val) {
114430                 if (!arguments.length) { return _key; }
114431                 _key = val;
114432                 return pane;
114433             };
114434
114435             pane.description = function(val) {
114436                 if (!arguments.length) { return _description; }
114437                 _description = val;
114438                 return pane;
114439             };
114440
114441             pane.iconName = function(val) {
114442                 if (!arguments.length) { return _iconName; }
114443                 _iconName = val;
114444                 return pane;
114445             };
114446
114447             pane.sections = function(val) {
114448                 if (!arguments.length) { return _sections; }
114449                 _sections = val;
114450                 return pane;
114451             };
114452
114453             pane.selection = function() {
114454                 return _paneSelection;
114455             };
114456
114457             function hidePane() {
114458                 context.ui().togglePanes();
114459             }
114460
114461             pane.togglePane = function() {
114462                 if (event) { event.preventDefault(); }
114463                 _paneTooltip.hide();
114464                 context.ui().togglePanes(!_paneSelection.classed('shown') ? _paneSelection : undefined);
114465             };
114466
114467             pane.renderToggleButton = function(selection) {
114468
114469                 if (!_paneTooltip) {
114470                     _paneTooltip = uiTooltip()
114471                         .placement((_mainLocalizer.textDirection() === 'rtl') ? 'right' : 'left')
114472                         .title(_description)
114473                         .keys([_key]);
114474                 }
114475
114476                 selection
114477                     .append('button')
114478                     .on('click', pane.togglePane)
114479                     .call(svgIcon('#' + _iconName, 'light'))
114480                     .call(_paneTooltip);
114481             };
114482
114483             pane.renderContent = function(selection) {
114484                 // override to fully customize content
114485
114486                 if (_sections) {
114487                     _sections.forEach(function(section) {
114488                         selection.call(section.render);
114489                     });
114490                 }
114491             };
114492
114493             pane.renderPane = function(selection) {
114494
114495                 _paneSelection = selection
114496                     .append('div')
114497                     .attr('class', 'fillL map-pane hide ' + id + '-pane')
114498                     .attr('pane', id);
114499
114500                 var heading = _paneSelection
114501                     .append('div')
114502                     .attr('class', 'pane-heading');
114503
114504                 heading
114505                     .append('h2')
114506                     .text(_title);
114507
114508                 heading
114509                     .append('button')
114510                     .on('click', hidePane)
114511                     .call(svgIcon('#iD-icon-close'));
114512
114513
114514                 _paneSelection
114515                     .append('div')
114516                     .attr('class', 'pane-content')
114517                     .call(pane.renderContent);
114518
114519                 if (_key) {
114520                     context.keybinding()
114521                         .on(_key, pane.togglePane);
114522                 }
114523             };
114524
114525             return pane;
114526         }
114527
114528         function uiSectionBackgroundDisplayOptions(context) {
114529
114530             var section = uiSection('background-display-options', context)
114531                 .title(_t('background.display_options'))
114532                 .disclosureContent(renderDisclosureContent);
114533
114534             var _detected = utilDetect();
114535             var _storedOpacity = corePreferences('background-opacity');
114536             var _minVal = 0.25;
114537             var _maxVal = _detected.cssfilters ? 2 : 1;
114538
114539             var _sliders = _detected.cssfilters
114540                 ? ['brightness', 'contrast', 'saturation', 'sharpness']
114541                 : ['brightness'];
114542
114543             var _options = {
114544                 brightness: (_storedOpacity !== null ? (+_storedOpacity) : 1),
114545                 contrast: 1,
114546                 saturation: 1,
114547                 sharpness: 1
114548             };
114549
114550             function clamp(x, min, max) {
114551                 return Math.max(min, Math.min(x, max));
114552             }
114553
114554             function updateValue(d, val) {
114555                 if (!val && event && event.target) {
114556                     val = event.target.value;
114557                 }
114558
114559                 val = clamp(val, _minVal, _maxVal);
114560
114561                 _options[d] = val;
114562                 context.background()[d](val);
114563
114564                 if (d === 'brightness') {
114565                     corePreferences('background-opacity', val);
114566                 }
114567
114568                 section.reRender();
114569             }
114570
114571             function renderDisclosureContent(selection) {
114572                 var container = selection.selectAll('.display-options-container')
114573                     .data([0]);
114574
114575                 var containerEnter = container.enter()
114576                     .append('div')
114577                     .attr('class', 'display-options-container controls-list');
114578
114579                 // add slider controls
114580                 var slidersEnter = containerEnter.selectAll('.display-control')
114581                     .data(_sliders)
114582                     .enter()
114583                     .append('div')
114584                     .attr('class', function(d) { return 'display-control display-control-' + d; });
114585
114586                 slidersEnter
114587                     .append('h5')
114588                     .text(function(d) { return _t('background.' + d); })
114589                     .append('span')
114590                     .attr('class', function(d) { return 'display-option-value display-option-value-' + d; });
114591
114592                 slidersEnter
114593                     .append('input')
114594                     .attr('class', function(d) { return 'display-option-input display-option-input-' + d; })
114595                     .attr('type', 'range')
114596                     .attr('min', _minVal)
114597                     .attr('max', _maxVal)
114598                     .attr('step', '0.05')
114599                     .on('input', function(d) {
114600                         var val = select(this).property('value');
114601                         updateValue(d, val);
114602                     });
114603
114604                 slidersEnter
114605                     .append('button')
114606                     .attr('title', _t('background.reset'))
114607                     .attr('class', function(d) { return 'display-option-reset display-option-reset-' + d; })
114608                     .on('click', function(d) {
114609                         if (event.button !== 0) { return; }
114610                         updateValue(d, 1);
114611                     })
114612                     .call(svgIcon('#iD-icon-' + (_mainLocalizer.textDirection() === 'rtl' ? 'redo' : 'undo')));
114613
114614                 // reset all button
114615                 containerEnter
114616                     .append('a')
114617                     .attr('class', 'display-option-resetlink')
114618                     .attr('href', '#')
114619                     .text(_t('background.reset_all'))
114620                     .on('click', function() {
114621                         for (var i = 0; i < _sliders.length; i++) {
114622                             updateValue(_sliders[i],1);
114623                         }
114624                     });
114625
114626                 // update
114627                 container = containerEnter
114628                     .merge(container);
114629
114630                 container.selectAll('.display-option-input')
114631                     .property('value', function(d) { return _options[d]; });
114632
114633                 container.selectAll('.display-option-value')
114634                     .text(function(d) { return Math.floor(_options[d] * 100) + '%'; });
114635
114636                 container.selectAll('.display-option-reset')
114637                     .classed('disabled', function(d) { return _options[d] === 1; });
114638
114639                 // first time only, set brightness if needed
114640                 if (containerEnter.size() && _options.brightness !== 1) {
114641                     context.background().brightness(_options.brightness);
114642                 }
114643             }
114644
114645             return section;
114646         }
114647
114648         function uiSettingsCustomBackground() {
114649             var dispatch$1 = dispatch('change');
114650
114651             function render(selection) {
114652                 // keep separate copies of original and current settings
114653                 var _origSettings = {
114654                     template: corePreferences('background-custom-template')
114655                 };
114656                 var _currSettings = {
114657                     template: corePreferences('background-custom-template')
114658                 };
114659
114660                 var example = 'https://{switch:a,b,c}.tile.openstreetmap.org/{zoom}/{x}/{y}.png';
114661                 var modal = uiConfirm(selection).okButton();
114662
114663                 modal
114664                     .classed('settings-modal settings-custom-background', true);
114665
114666                 modal.select('.modal-section.header')
114667                     .append('h3')
114668                     .text(_t('settings.custom_background.header'));
114669
114670
114671                 var textSection = modal.select('.modal-section.message-text');
114672
114673                 var instructions =
114674                     (_t('settings.custom_background.instructions.info')) + "\n" +
114675                     '\n' +
114676                     "#### " + (_t('settings.custom_background.instructions.wms.tokens_label')) + "\n" +
114677                     "* " + (_t('settings.custom_background.instructions.wms.tokens.proj')) + "\n" +
114678                     "* " + (_t('settings.custom_background.instructions.wms.tokens.wkid')) + "\n" +
114679                     "* " + (_t('settings.custom_background.instructions.wms.tokens.dimensions')) + "\n" +
114680                     "* " + (_t('settings.custom_background.instructions.wms.tokens.bbox')) + "\n" +
114681                     '\n' +
114682                     "#### " + (_t('settings.custom_background.instructions.tms.tokens_label')) + "\n" +
114683                     "* " + (_t('settings.custom_background.instructions.tms.tokens.xyz')) + "\n" +
114684                     "* " + (_t('settings.custom_background.instructions.tms.tokens.flipped_y')) + "\n" +
114685                     "* " + (_t('settings.custom_background.instructions.tms.tokens.switch')) + "\n" +
114686                     "* " + (_t('settings.custom_background.instructions.tms.tokens.quadtile')) + "\n" +
114687                     "* " + (_t('settings.custom_background.instructions.tms.tokens.scale_factor')) + "\n" +
114688                     '\n' +
114689                     "#### " + (_t('settings.custom_background.instructions.example')) + "\n" +
114690                     "`" + example + "`";
114691
114692                 textSection
114693                     .append('div')
114694                     .attr('class', 'instructions-template')
114695                     .html(marked_1(instructions));
114696
114697                 textSection
114698                     .append('textarea')
114699                     .attr('class', 'field-template')
114700                     .attr('placeholder', _t('settings.custom_background.template.placeholder'))
114701                     .call(utilNoAuto)
114702                     .property('value', _currSettings.template);
114703
114704
114705                 // insert a cancel button
114706                 var buttonSection = modal.select('.modal-section.buttons');
114707
114708                 buttonSection
114709                     .insert('button', '.ok-button')
114710                     .attr('class', 'button cancel-button secondary-action')
114711                     .text(_t('confirm.cancel'));
114712
114713
114714                 buttonSection.select('.cancel-button')
114715                     .on('click.cancel', clickCancel);
114716
114717                 buttonSection.select('.ok-button')
114718                     .attr('disabled', isSaveDisabled)
114719                     .on('click.save', clickSave);
114720
114721
114722                 function isSaveDisabled() {
114723                     return null;
114724                 }
114725
114726
114727                 // restore the original template
114728                 function clickCancel() {
114729                     textSection.select('.field-template').property('value', _origSettings.template);
114730                     corePreferences('background-custom-template', _origSettings.template);
114731                     this.blur();
114732                     modal.close();
114733                 }
114734
114735                 // accept the current template
114736                 function clickSave() {
114737                     _currSettings.template = textSection.select('.field-template').property('value');
114738                     corePreferences('background-custom-template', _currSettings.template);
114739                     this.blur();
114740                     modal.close();
114741                     dispatch$1.call('change', this, _currSettings);
114742                 }
114743             }
114744
114745             return utilRebind(render, dispatch$1, 'on');
114746         }
114747
114748         function uiSectionBackgroundList(context) {
114749
114750             var _backgroundList = select(null);
114751
114752             var _customSource = context.background().findSource('custom');
114753
114754             var _settingsCustomBackground = uiSettingsCustomBackground()
114755                 .on('change', customChanged);
114756
114757             var section = uiSection('background-list', context)
114758                 .title(_t('background.backgrounds'))
114759                 .disclosureContent(renderDisclosureContent);
114760
114761             function previousBackgroundID() {
114762                 return corePreferences('background-last-used-toggle');
114763             }
114764
114765             function renderDisclosureContent(selection) {
114766
114767                 // the background list
114768                 var container = selection.selectAll('.layer-background-list')
114769                     .data([0]);
114770
114771                 _backgroundList = container.enter()
114772                     .append('ul')
114773                     .attr('class', 'layer-list layer-background-list')
114774                     .attr('dir', 'auto')
114775                     .merge(container);
114776
114777
114778                 // add minimap toggle below list
114779                 var bgExtrasListEnter = selection.selectAll('.bg-extras-list')
114780                     .data([0])
114781                     .enter()
114782                     .append('ul')
114783                     .attr('class', 'layer-list bg-extras-list');
114784
114785                 var minimapLabelEnter = bgExtrasListEnter
114786                     .append('li')
114787                     .attr('class', 'minimap-toggle-item')
114788                     .append('label')
114789                     .call(uiTooltip()
114790                         .title(_t('background.minimap.tooltip'))
114791                         .keys([_t('background.minimap.key')])
114792                         .placement('top')
114793                     );
114794
114795                 minimapLabelEnter
114796                     .append('input')
114797                     .attr('type', 'checkbox')
114798                     .on('change', function() {
114799                         event.preventDefault();
114800                         uiMapInMap.toggle();
114801                     });
114802
114803                 minimapLabelEnter
114804                     .append('span')
114805                     .text(_t('background.minimap.description'));
114806
114807
114808                 var panelLabelEnter = bgExtrasListEnter
114809                     .append('li')
114810                     .attr('class', 'background-panel-toggle-item')
114811                     .append('label')
114812                     .call(uiTooltip()
114813                         .title(_t('background.panel.tooltip'))
114814                         .keys([uiCmd('⌘⇧' + _t('info_panels.background.key'))])
114815                         .placement('top')
114816                     );
114817
114818                 panelLabelEnter
114819                     .append('input')
114820                     .attr('type', 'checkbox')
114821                     .on('change', function() {
114822                         event.preventDefault();
114823                         context.ui().info.toggle('background');
114824                     });
114825
114826                 panelLabelEnter
114827                     .append('span')
114828                     .text(_t('background.panel.description'));
114829
114830                 var locPanelLabelEnter = bgExtrasListEnter
114831                     .append('li')
114832                     .attr('class', 'location-panel-toggle-item')
114833                     .append('label')
114834                     .call(uiTooltip()
114835                         .title(_t('background.location_panel.tooltip'))
114836                         .keys([uiCmd('⌘⇧' + _t('info_panels.location.key'))])
114837                         .placement('top')
114838                     );
114839
114840                 locPanelLabelEnter
114841                     .append('input')
114842                     .attr('type', 'checkbox')
114843                     .on('change', function() {
114844                         event.preventDefault();
114845                         context.ui().info.toggle('location');
114846                     });
114847
114848                 locPanelLabelEnter
114849                     .append('span')
114850                     .text(_t('background.location_panel.description'));
114851
114852
114853                 // "Info / Report a Problem" link
114854                 selection.selectAll('.imagery-faq')
114855                     .data([0])
114856                     .enter()
114857                     .append('div')
114858                     .attr('class', 'imagery-faq')
114859                     .append('a')
114860                     .attr('target', '_blank')
114861                     .call(svgIcon('#iD-icon-out-link', 'inline'))
114862                     .attr('href', 'https://github.com/openstreetmap/iD/blob/develop/FAQ.md#how-can-i-report-an-issue-with-background-imagery')
114863                     .append('span')
114864                     .text(_t('background.imagery_problem_faq'));
114865
114866                 _backgroundList
114867                     .call(drawListItems, 'radio', chooseBackground, function(d) { return !d.isHidden() && !d.overlay; });
114868             }
114869
114870             function setTooltips(selection) {
114871                 selection.each(function(d, i, nodes) {
114872                     var item = select(this).select('label');
114873                     var span = item.select('span');
114874                     var placement = (i < nodes.length / 2) ? 'bottom' : 'top';
114875                     var description = d.description();
114876                     var isOverflowing = (span.property('clientWidth') !== span.property('scrollWidth'));
114877
114878                     item.call(uiTooltip().destroyAny);
114879
114880                     if (d.id === previousBackgroundID()) {
114881                         item.call(uiTooltip()
114882                             .placement(placement)
114883                             .title('<div>' + _t('background.switch') + '</div>')
114884                             .keys([uiCmd('⌘' + _t('background.key'))])
114885                         );
114886                     } else if (description || isOverflowing) {
114887                         item.call(uiTooltip()
114888                             .placement(placement)
114889                             .title(description || d.name())
114890                         );
114891                     }
114892                 });
114893             }
114894
114895             function drawListItems(layerList, type, change, filter) {
114896                 var sources = context.background()
114897                     .sources(context.map().extent(), context.map().zoom(), true)
114898                     .filter(filter);
114899
114900                 var layerLinks = layerList.selectAll('li')
114901                     .data(sources, function(d) { return d.name(); });
114902
114903                 layerLinks.exit()
114904                     .remove();
114905
114906                 var enter = layerLinks.enter()
114907                     .append('li')
114908                     .classed('layer-custom', function(d) { return d.id === 'custom'; })
114909                     .classed('best', function(d) { return d.best(); });
114910
114911                 var label = enter
114912                     .append('label');
114913
114914                 label
114915                     .append('input')
114916                     .attr('type', type)
114917                     .attr('name', 'layers')
114918                     .on('change', change);
114919
114920                 label
114921                     .append('span')
114922                     .text(function(d) { return d.name(); });
114923
114924                 enter.filter(function(d) { return d.id === 'custom'; })
114925                     .append('button')
114926                     .attr('class', 'layer-browse')
114927                     .call(uiTooltip()
114928                         .title(_t('settings.custom_background.tooltip'))
114929                         .placement((_mainLocalizer.textDirection() === 'rtl') ? 'right' : 'left')
114930                     )
114931                     .on('click', editCustom)
114932                     .call(svgIcon('#iD-icon-more'));
114933
114934                 enter.filter(function(d) { return d.best(); })
114935                     .append('div')
114936                     .attr('class', 'best')
114937                     .call(uiTooltip()
114938                         .title(_t('background.best_imagery'))
114939                         .placement((_mainLocalizer.textDirection() === 'rtl') ? 'right' : 'left')
114940                     )
114941                     .append('span')
114942                     .html('&#9733;');
114943
114944
114945                 layerList.selectAll('li')
114946                     .sort(sortSources);
114947
114948                 layerList
114949                     .call(updateLayerSelections);
114950
114951
114952                 function sortSources(a, b) {
114953                     return a.best() && !b.best() ? -1
114954                         : b.best() && !a.best() ? 1
114955                         : d3_descending(a.area(), b.area()) || d3_ascending(a.name(), b.name()) || 0;
114956                 }
114957             }
114958
114959             function updateLayerSelections(selection) {
114960                 function active(d) {
114961                     return context.background().showsLayer(d);
114962                 }
114963
114964                 selection.selectAll('li')
114965                     .classed('active', active)
114966                     .classed('switch', function(d) { return d.id === previousBackgroundID(); })
114967                     .call(setTooltips)
114968                     .selectAll('input')
114969                     .property('checked', active);
114970             }
114971
114972
114973             function chooseBackground(d) {
114974                 if (d.id === 'custom' && !d.template()) {
114975                     return editCustom();
114976                 }
114977
114978                 event.preventDefault();
114979                 var previousBackground = context.background().baseLayerSource();
114980                 corePreferences('background-last-used-toggle', previousBackground.id);
114981                 corePreferences('background-last-used', d.id);
114982                 context.background().baseLayerSource(d);
114983                 document.activeElement.blur();
114984             }
114985
114986
114987             function customChanged(d) {
114988                 if (d && d.template) {
114989                     _customSource.template(d.template);
114990                     chooseBackground(_customSource);
114991                 } else {
114992                     _customSource.template('');
114993                     chooseBackground(context.background().findSource('none'));
114994                 }
114995             }
114996
114997
114998             function editCustom() {
114999                 event.preventDefault();
115000                 context.container()
115001                     .call(_settingsCustomBackground);
115002             }
115003
115004
115005             context.background()
115006                 .on('change.background_list', function() {
115007                     _backgroundList.call(updateLayerSelections);
115008                 });
115009
115010             context.map()
115011                 .on('move.background_list',
115012                     debounce(function() {
115013                         // layers in-view may have changed due to map move
115014                         window.requestIdleCallback(section.reRender);
115015                     }, 1000)
115016                 );
115017
115018             return section;
115019         }
115020
115021         function uiSectionBackgroundOffset(context) {
115022
115023             var section = uiSection('background-offset', context)
115024                 .title(_t('background.fix_misalignment'))
115025                 .disclosureContent(renderDisclosureContent)
115026                 .expandedByDefault(false);
115027
115028             var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
115029
115030             var _directions = [
115031                 ['right', [0.5, 0]],
115032                 ['top', [0, -0.5]],
115033                 ['left', [-0.5, 0]],
115034                 ['bottom', [0, 0.5]]
115035             ];
115036
115037
115038             function cancelEvent() {
115039                 event.stopPropagation();
115040                 event.preventDefault();
115041             }
115042
115043
115044             function updateValue() {
115045                 var meters = geoOffsetToMeters(context.background().offset());
115046                 var x = +meters[0].toFixed(2);
115047                 var y = +meters[1].toFixed(2);
115048
115049                 context.container().selectAll('.nudge-inner-rect')
115050                     .select('input')
115051                     .classed('error', false)
115052                     .property('value', x + ', ' + y);
115053
115054                 context.container().selectAll('.nudge-reset')
115055                     .classed('disabled', function() {
115056                         return (x === 0 && y === 0);
115057                     });
115058             }
115059
115060
115061             function resetOffset() {
115062                 context.background().offset([0, 0]);
115063                 updateValue();
115064             }
115065
115066
115067             function nudge(d) {
115068                 context.background().nudge(d, context.map().zoom());
115069                 updateValue();
115070             }
115071
115072
115073             function pointerdownNudgeButton(d) {
115074                 var interval;
115075                 var timeout = window.setTimeout(function() {
115076                         interval = window.setInterval(nudge.bind(null, d), 100);
115077                     }, 500);
115078
115079                 function doneNudge() {
115080                     window.clearTimeout(timeout);
115081                     window.clearInterval(interval);
115082                     select(window)
115083                         .on(_pointerPrefix + 'up.buttonoffset', null, true)
115084                         .on(_pointerPrefix + 'down.buttonoffset', null, true);
115085                 }
115086
115087                 select(window)
115088                     .on(_pointerPrefix + 'up.buttonoffset', doneNudge, true)
115089                     .on(_pointerPrefix + 'down.buttonoffset', doneNudge, true);
115090
115091                 nudge(d);
115092             }
115093
115094
115095             function inputOffset() {
115096                 var input = select(this);
115097                 var d = input.node().value;
115098
115099                 if (d === '') { return resetOffset(); }
115100
115101                 d = d.replace(/;/g, ',').split(',').map(function(n) {
115102                     // if n is NaN, it will always get mapped to false.
115103                     return !isNaN(n) && n;
115104                 });
115105
115106                 if (d.length !== 2 || !d[0] || !d[1]) {
115107                     input.classed('error', true);
115108                     return;
115109                 }
115110
115111                 context.background().offset(geoMetersToOffset(d));
115112                 updateValue();
115113             }
115114
115115
115116             function dragOffset() {
115117                 if (event.button !== 0) { return; }
115118
115119                 var origin = [event.clientX, event.clientY];
115120
115121                 var pointerId = event.pointerId || 'mouse';
115122
115123                 context.container()
115124                     .append('div')
115125                     .attr('class', 'nudge-surface');
115126
115127                 select(window)
115128                     .on(_pointerPrefix + 'move.drag-bg-offset', pointermove)
115129                     .on(_pointerPrefix + 'up.drag-bg-offset', pointerup);
115130
115131                 if (_pointerPrefix === 'pointer') {
115132                     select(window)
115133                         .on('pointercancel.drag-bg-offset', pointerup);
115134                 }
115135
115136                 function pointermove() {
115137                     if (pointerId !== (event.pointerId || 'mouse')) { return; }
115138
115139                     var latest = [event.clientX, event.clientY];
115140                     var d = [
115141                         -(origin[0] - latest[0]) / 4,
115142                         -(origin[1] - latest[1]) / 4
115143                     ];
115144
115145                     origin = latest;
115146                     nudge(d);
115147                 }
115148
115149                 function pointerup() {
115150                     if (pointerId !== (event.pointerId || 'mouse')) { return; }
115151                     if (event.button !== 0) { return; }
115152
115153                     context.container().selectAll('.nudge-surface')
115154                         .remove();
115155
115156                     select(window)
115157                         .on('.drag-bg-offset', null);
115158                 }
115159             }
115160
115161
115162             function renderDisclosureContent(selection) {
115163                 var container = selection.selectAll('.nudge-container')
115164                     .data([0]);
115165
115166                 var containerEnter = container.enter()
115167                     .append('div')
115168                     .attr('class', 'nudge-container cf');
115169
115170                 containerEnter
115171                     .append('div')
115172                     .attr('class', 'nudge-instructions')
115173                     .text(_t('background.offset'));
115174
115175                 var nudgeEnter = containerEnter
115176                     .append('div')
115177                     .attr('class', 'nudge-outer-rect')
115178                     .on(_pointerPrefix + 'down', dragOffset);
115179
115180                 nudgeEnter
115181                     .append('div')
115182                     .attr('class', 'nudge-inner-rect')
115183                     .append('input')
115184                     .on('change', inputOffset);
115185
115186                 containerEnter
115187                     .append('div')
115188                     .selectAll('button')
115189                     .data(_directions).enter()
115190                     .append('button')
115191                     .attr('class', function(d) { return d[0] + ' nudge'; })
115192                     .on('contextmenu', cancelEvent)
115193                     .on(_pointerPrefix + 'down', function(d) {
115194                         if (event.button !== 0) { return; }
115195                         pointerdownNudgeButton(d[1]);
115196                     });
115197
115198                 containerEnter
115199                     .append('button')
115200                     .attr('title', _t('background.reset'))
115201                     .attr('class', 'nudge-reset disabled')
115202                     .on('contextmenu', cancelEvent)
115203                     .on('click', function() {
115204                         event.preventDefault();
115205                         if (event.button !== 0) { return; }
115206                         resetOffset();
115207                     })
115208                     .call(svgIcon('#iD-icon-' + (_mainLocalizer.textDirection() === 'rtl' ? 'redo' : 'undo')));
115209
115210                 updateValue();
115211             }
115212
115213             context.background()
115214                 .on('change.backgroundOffset-update', updateValue);
115215
115216             return section;
115217         }
115218
115219         function uiSectionOverlayList(context) {
115220
115221             var section = uiSection('overlay-list', context)
115222                 .title(_t('background.overlays'))
115223                 .disclosureContent(renderDisclosureContent);
115224
115225             var _overlayList = select(null);
115226
115227             function setTooltips(selection) {
115228                 selection.each(function(d, i, nodes) {
115229                     var item = select(this).select('label');
115230                     var span = item.select('span');
115231                     var placement = (i < nodes.length / 2) ? 'bottom' : 'top';
115232                     var description = d.description();
115233                     var isOverflowing = (span.property('clientWidth') !== span.property('scrollWidth'));
115234
115235                     item.call(uiTooltip().destroyAny);
115236
115237                     if (description || isOverflowing) {
115238                         item.call(uiTooltip()
115239                             .placement(placement)
115240                             .title(description || d.name())
115241                         );
115242                     }
115243                 });
115244             }
115245
115246             function updateLayerSelections(selection) {
115247                 function active(d) {
115248                     return context.background().showsLayer(d);
115249                 }
115250
115251                 selection.selectAll('li')
115252                     .classed('active', active)
115253                     .call(setTooltips)
115254                     .selectAll('input')
115255                     .property('checked', active);
115256             }
115257
115258
115259             function chooseOverlay(d) {
115260                 event.preventDefault();
115261                 context.background().toggleOverlayLayer(d);
115262                 _overlayList.call(updateLayerSelections);
115263                 document.activeElement.blur();
115264             }
115265
115266             function drawListItems(layerList, type, change, filter) {
115267                 var sources = context.background()
115268                     .sources(context.map().extent(), context.map().zoom(), true)
115269                     .filter(filter);
115270
115271                 var layerLinks = layerList.selectAll('li')
115272                     .data(sources, function(d) { return d.name(); });
115273
115274                 layerLinks.exit()
115275                     .remove();
115276
115277                 var enter = layerLinks.enter()
115278                     .append('li');
115279
115280                 var label = enter
115281                     .append('label');
115282
115283                 label
115284                     .append('input')
115285                     .attr('type', type)
115286                     .attr('name', 'layers')
115287                     .on('change', change);
115288
115289                 label
115290                     .append('span')
115291                     .text(function(d) { return d.name(); });
115292
115293
115294                 layerList.selectAll('li')
115295                     .sort(sortSources);
115296
115297                 layerList
115298                     .call(updateLayerSelections);
115299
115300
115301                 function sortSources(a, b) {
115302                     return a.best() && !b.best() ? -1
115303                         : b.best() && !a.best() ? 1
115304                         : d3_descending(a.area(), b.area()) || d3_ascending(a.name(), b.name()) || 0;
115305                 }
115306             }
115307
115308             function renderDisclosureContent(selection) {
115309
115310                 var container = selection.selectAll('.layer-overlay-list')
115311                     .data([0]);
115312
115313                 _overlayList = container.enter()
115314                     .append('ul')
115315                     .attr('class', 'layer-list layer-overlay-list')
115316                     .attr('dir', 'auto')
115317                     .merge(container);
115318
115319                 _overlayList
115320                     .call(drawListItems, 'checkbox', chooseOverlay, function(d) { return !d.isHidden() && d.overlay; });
115321             }
115322
115323             context.map()
115324                 .on('move.overlay_list',
115325                     debounce(function() {
115326                         // layers in-view may have changed due to map move
115327                         window.requestIdleCallback(section.reRender);
115328                     }, 1000)
115329                 );
115330
115331             return section;
115332         }
115333
115334         function uiPaneBackground(context) {
115335
115336             var backgroundPane = uiPane('background', context)
115337                 .key(_t('background.key'))
115338                 .title(_t('background.title'))
115339                 .description(_t('background.description'))
115340                 .iconName('iD-icon-layers')
115341                 .sections([
115342                     uiSectionBackgroundList(context),
115343                     uiSectionOverlayList(context),
115344                     uiSectionBackgroundDisplayOptions(context),
115345                     uiSectionBackgroundOffset(context)
115346                 ]);
115347
115348             return backgroundPane;
115349         }
115350
115351         function uiPaneHelp(context) {
115352
115353             var docKeys = [
115354                 ['help', [
115355                     'welcome',
115356                     'open_data_h',
115357                     'open_data',
115358                     'before_start_h',
115359                     'before_start',
115360                     'open_source_h',
115361                     'open_source',
115362                     'open_source_help'
115363                 ]],
115364                 ['overview', [
115365                     'navigation_h',
115366                     'navigation_drag',
115367                     'navigation_zoom',
115368                     'features_h',
115369                     'features',
115370                     'nodes_ways'
115371                 ]],
115372                 ['editing', [
115373                     'select_h',
115374                     'select_left_click',
115375                     'select_right_click',
115376                     'select_space',
115377                     'multiselect_h',
115378                     'multiselect',
115379                     'multiselect_shift_click',
115380                     'multiselect_lasso',
115381                     'undo_redo_h',
115382                     'undo_redo',
115383                     'save_h',
115384                     'save',
115385                     'save_validation',
115386                     'upload_h',
115387                     'upload',
115388                     'backups_h',
115389                     'backups',
115390                     'keyboard_h',
115391                     'keyboard'
115392                 ]],
115393                 ['feature_editor', [
115394                     'intro',
115395                     'definitions',
115396                     'type_h',
115397                     'type',
115398                     'type_picker',
115399                     'fields_h',
115400                     'fields_all_fields',
115401                     'fields_example',
115402                     'fields_add_field',
115403                     'tags_h',
115404                     'tags_all_tags',
115405                     'tags_resources'
115406                 ]],
115407                 ['points', [
115408                     'intro',
115409                     'add_point_h',
115410                     'add_point',
115411                     'add_point_finish',
115412                     'move_point_h',
115413                     'move_point',
115414                     'delete_point_h',
115415                     'delete_point',
115416                     'delete_point_command'
115417                 ]],
115418                 ['lines', [
115419                     'intro',
115420                     'add_line_h',
115421                     'add_line',
115422                     'add_line_draw',
115423                     'add_line_continue',
115424                     'add_line_finish',
115425                     'modify_line_h',
115426                     'modify_line_dragnode',
115427                     'modify_line_addnode',
115428                     'connect_line_h',
115429                     'connect_line',
115430                     'connect_line_display',
115431                     'connect_line_drag',
115432                     'connect_line_tag',
115433                     'disconnect_line_h',
115434                     'disconnect_line_command',
115435                     'move_line_h',
115436                     'move_line_command',
115437                     'move_line_connected',
115438                     'delete_line_h',
115439                     'delete_line',
115440                     'delete_line_command'
115441                 ]],
115442                 ['areas', [
115443                     'intro',
115444                     'point_or_area_h',
115445                     'point_or_area',
115446                     'add_area_h',
115447                     'add_area_command',
115448                     'add_area_draw',
115449                     'add_area_continue',
115450                     'add_area_finish',
115451                     'square_area_h',
115452                     'square_area_command',
115453                     'modify_area_h',
115454                     'modify_area_dragnode',
115455                     'modify_area_addnode',
115456                     'delete_area_h',
115457                     'delete_area',
115458                     'delete_area_command'
115459                 ]],
115460                 ['relations', [
115461                     'intro',
115462                     'edit_relation_h',
115463                     'edit_relation',
115464                     'edit_relation_add',
115465                     'edit_relation_delete',
115466                     'maintain_relation_h',
115467                     'maintain_relation',
115468                     'relation_types_h',
115469                     'multipolygon_h',
115470                     'multipolygon',
115471                     'multipolygon_create',
115472                     'multipolygon_merge',
115473                     'turn_restriction_h',
115474                     'turn_restriction',
115475                     'turn_restriction_field',
115476                     'turn_restriction_editing',
115477                     'route_h',
115478                     'route',
115479                     'route_add',
115480                     'boundary_h',
115481                     'boundary',
115482                     'boundary_add'
115483                 ]],
115484                 ['notes', [
115485                     'intro',
115486                     'add_note_h',
115487                     'add_note',
115488                     'place_note',
115489                     'move_note',
115490                     'update_note_h',
115491                     'update_note',
115492                     'save_note_h',
115493                     'save_note'
115494                 ]],
115495                 ['imagery', [
115496                     'intro',
115497                     'sources_h',
115498                     'choosing',
115499                     'sources',
115500                     'offsets_h',
115501                     'offset',
115502                     'offset_change'
115503                 ]],
115504                 ['streetlevel', [
115505                     'intro',
115506                     'using_h',
115507                     'using',
115508                     'photos',
115509                     'viewer'
115510                 ]],
115511                 ['gps', [
115512                     'intro',
115513                     'survey',
115514                     'using_h',
115515                     'using',
115516                     'tracing',
115517                     'upload'
115518                 ]],
115519                 ['qa', [
115520                     'intro',
115521                     'tools_h',
115522                     'tools',
115523                     'issues_h',
115524                     'issues'
115525                 ]]
115526             ];
115527
115528             var headings = {
115529                 'help.help.open_data_h': 3,
115530                 'help.help.before_start_h': 3,
115531                 'help.help.open_source_h': 3,
115532                 'help.overview.navigation_h': 3,
115533                 'help.overview.features_h': 3,
115534                 'help.editing.select_h': 3,
115535                 'help.editing.multiselect_h': 3,
115536                 'help.editing.undo_redo_h': 3,
115537                 'help.editing.save_h': 3,
115538                 'help.editing.upload_h': 3,
115539                 'help.editing.backups_h': 3,
115540                 'help.editing.keyboard_h': 3,
115541                 'help.feature_editor.type_h': 3,
115542                 'help.feature_editor.fields_h': 3,
115543                 'help.feature_editor.tags_h': 3,
115544                 'help.points.add_point_h': 3,
115545                 'help.points.move_point_h': 3,
115546                 'help.points.delete_point_h': 3,
115547                 'help.lines.add_line_h': 3,
115548                 'help.lines.modify_line_h': 3,
115549                 'help.lines.connect_line_h': 3,
115550                 'help.lines.disconnect_line_h': 3,
115551                 'help.lines.move_line_h': 3,
115552                 'help.lines.delete_line_h': 3,
115553                 'help.areas.point_or_area_h': 3,
115554                 'help.areas.add_area_h': 3,
115555                 'help.areas.square_area_h': 3,
115556                 'help.areas.modify_area_h': 3,
115557                 'help.areas.delete_area_h': 3,
115558                 'help.relations.edit_relation_h': 3,
115559                 'help.relations.maintain_relation_h': 3,
115560                 'help.relations.relation_types_h': 2,
115561                 'help.relations.multipolygon_h': 3,
115562                 'help.relations.turn_restriction_h': 3,
115563                 'help.relations.route_h': 3,
115564                 'help.relations.boundary_h': 3,
115565                 'help.notes.add_note_h': 3,
115566                 'help.notes.update_note_h': 3,
115567                 'help.notes.save_note_h': 3,
115568                 'help.imagery.sources_h': 3,
115569                 'help.imagery.offsets_h': 3,
115570                 'help.streetlevel.using_h': 3,
115571                 'help.gps.using_h': 3,
115572                 'help.qa.tools_h': 3,
115573                 'help.qa.issues_h': 3
115574             };
115575
115576             // For each section, squash all the texts into a single markdown document
115577             var docs = docKeys.map(function(key) {
115578                 var helpkey = 'help.' + key[0];
115579                 var helpPaneReplacements = { version: context.version };
115580                 var text = key[1].reduce(function(all, part) {
115581                     var subkey = helpkey + '.' + part;
115582                     var depth = headings[subkey];                              // is this subkey a heading?
115583                     var hhh = depth ? Array(depth + 1).join('#') + ' ' : '';   // if so, prepend with some ##'s
115584                     return all + hhh + helpString(subkey, helpPaneReplacements) + '\n\n';
115585                 }, '');
115586
115587                 return {
115588                     title: _t(helpkey + '.title'),
115589                     html: marked_1(text.trim())
115590                         // use keyboard key styling for shortcuts
115591                         .replace(/<code>/g, '<kbd>')
115592                         .replace(/<\/code>/g, '<\/kbd>')
115593                 };
115594             });
115595
115596             var helpPane = uiPane('help', context)
115597                 .key(_t('help.key'))
115598                 .title(_t('help.title'))
115599                 .description(_t('help.title'))
115600                 .iconName('iD-icon-help');
115601
115602             helpPane.renderContent = function(content) {
115603
115604                 function clickHelp(d, i) {
115605                     var rtl = (_mainLocalizer.textDirection() === 'rtl');
115606                     content.property('scrollTop', 0);
115607                     helpPane.selection().select('.pane-heading h2').html(d.title);
115608
115609                     body.html(d.html);
115610                     body.selectAll('a')
115611                         .attr('target', '_blank');
115612                     menuItems.classed('selected', function(m) {
115613                         return m.title === d.title;
115614                     });
115615
115616                     nav.html('');
115617                     if (rtl) {
115618                         nav.call(drawNext).call(drawPrevious);
115619                     } else {
115620                         nav.call(drawPrevious).call(drawNext);
115621                     }
115622
115623
115624                     function drawNext(selection) {
115625                         if (i < docs.length - 1) {
115626                             var nextLink = selection
115627                                 .append('a')
115628                                 .attr('class', 'next')
115629                                 .on('click', function() {
115630                                     clickHelp(docs[i + 1], i + 1);
115631                                 });
115632
115633                             nextLink
115634                                 .append('span')
115635                                 .text(docs[i + 1].title)
115636                                 .call(svgIcon((rtl ? '#iD-icon-backward' : '#iD-icon-forward'), 'inline'));
115637                         }
115638                     }
115639
115640
115641                     function drawPrevious(selection) {
115642                         if (i > 0) {
115643                             var prevLink = selection
115644                                 .append('a')
115645                                 .attr('class', 'previous')
115646                                 .on('click', function() {
115647                                     clickHelp(docs[i - 1], i - 1);
115648                                 });
115649
115650                             prevLink
115651                                 .call(svgIcon((rtl ? '#iD-icon-forward' : '#iD-icon-backward'), 'inline'))
115652                                 .append('span')
115653                                 .text(docs[i - 1].title);
115654                         }
115655                     }
115656                 }
115657
115658
115659                 function clickWalkthrough() {
115660                     if (context.inIntro()) { return; }
115661                     context.container().call(uiIntro(context));
115662                     context.ui().togglePanes();
115663                 }
115664
115665
115666                 function clickShortcuts() {
115667                     context.container().call(uiShortcuts(context), true);
115668                 }
115669
115670                 var toc = content
115671                     .append('ul')
115672                     .attr('class', 'toc');
115673
115674                 var menuItems = toc.selectAll('li')
115675                     .data(docs)
115676                     .enter()
115677                     .append('li')
115678                     .append('a')
115679                     .html(function(d) { return d.title; })
115680                     .on('click', clickHelp);
115681
115682                 var shortcuts = toc
115683                     .append('li')
115684                     .attr('class', 'shortcuts')
115685                     .call(uiTooltip()
115686                         .title(_t('shortcuts.tooltip'))
115687                         .keys(['?'])
115688                         .placement('top')
115689                     )
115690                     .append('a')
115691                     .on('click', clickShortcuts);
115692
115693                 shortcuts
115694                     .append('div')
115695                     .text(_t('shortcuts.title'));
115696
115697                 var walkthrough = toc
115698                     .append('li')
115699                     .attr('class', 'walkthrough')
115700                     .append('a')
115701                     .on('click', clickWalkthrough);
115702
115703                 walkthrough
115704                     .append('svg')
115705                     .attr('class', 'logo logo-walkthrough')
115706                     .append('use')
115707                     .attr('xlink:href', '#iD-logo-walkthrough');
115708
115709                 walkthrough
115710                     .append('div')
115711                     .text(_t('splash.walkthrough'));
115712
115713
115714                 var helpContent = content
115715                     .append('div')
115716                     .attr('class', 'left-content');
115717
115718                 var body = helpContent
115719                     .append('div')
115720                     .attr('class', 'body');
115721
115722                 var nav = helpContent
115723                     .append('div')
115724                     .attr('class', 'nav');
115725
115726                 clickHelp(docs[0], 0);
115727
115728             };
115729
115730             return helpPane;
115731         }
115732
115733         function uiSectionValidationIssues(id, severity, context) {
115734
115735             var _issues = [];
115736
115737             var section = uiSection(id, context)
115738                 .title(function() {
115739                     if (!_issues) { return ''; }
115740                     var issueCountText = _issues.length > 1000 ? '1000+' : String(_issues.length);
115741                     return _t('issues.' + severity + 's.list_title', { count: issueCountText });
115742                 })
115743                 .disclosureContent(renderDisclosureContent)
115744                 .shouldDisplay(function() {
115745                     return _issues && _issues.length;
115746                 });
115747
115748             function getOptions() {
115749                 return {
115750                     what: corePreferences('validate-what') || 'edited',
115751                     where: corePreferences('validate-where') || 'all'
115752                 };
115753             }
115754
115755             // get and cache the issues to display, unordered
115756             function reloadIssues() {
115757                 _issues = context.validator().getIssuesBySeverity(getOptions())[severity];
115758             }
115759
115760             function renderDisclosureContent(selection) {
115761
115762                 var center = context.map().center();
115763                 var graph = context.graph();
115764
115765                 // sort issues by distance away from the center of the map
115766                 var issues = _issues.map(function withDistance(issue) {
115767                         var extent = issue.extent(graph);
115768                         var dist = extent ? geoSphericalDistance(center, extent.center()) : 0;
115769                         return Object.assign(issue, { dist: dist });
115770                     })
115771                     .sort(function byDistance(a, b) {
115772                         return a.dist - b.dist;
115773                     });
115774
115775                 // cut off at 1000
115776                 issues = issues.slice(0, 1000);
115777
115778                 //renderIgnoredIssuesReset(_warningsSelection);
115779
115780                 selection
115781                     .call(drawIssuesList, issues);
115782             }
115783
115784             function drawIssuesList(selection, issues) {
115785                 var list = selection.selectAll('.issues-list')
115786                     .data([0]);
115787
115788                 list = list.enter()
115789                     .append('ul')
115790                     .attr('class', 'layer-list issues-list ' + severity + 's-list')
115791                     .merge(list);
115792
115793
115794                 var items = list.selectAll('li')
115795                     .data(issues, function(d) { return d.id; });
115796
115797                 // Exit
115798                 items.exit()
115799                     .remove();
115800
115801                 // Enter
115802                 var itemsEnter = items.enter()
115803                     .append('li')
115804                     .attr('class', function (d) { return 'issue severity-' + d.severity; })
115805                     .on('click', function(d) {
115806                         context.validator().focusIssue(d);
115807                     })
115808                     .on('mouseover', function(d) {
115809                         utilHighlightEntities(d.entityIds, true, context);
115810                     })
115811                     .on('mouseout', function(d) {
115812                         utilHighlightEntities(d.entityIds, false, context);
115813                     });
115814
115815
115816                 var labelsEnter = itemsEnter
115817                     .append('div')
115818                     .attr('class', 'issue-label');
115819
115820                 var textEnter = labelsEnter
115821                     .append('span')
115822                     .attr('class', 'issue-text');
115823
115824                 textEnter
115825                     .append('span')
115826                     .attr('class', 'issue-icon')
115827                     .each(function(d) {
115828                         var iconName = '#iD-icon-' + (d.severity === 'warning' ? 'alert' : 'error');
115829                         select(this)
115830                             .call(svgIcon(iconName));
115831                     });
115832
115833                 textEnter
115834                     .append('span')
115835                     .attr('class', 'issue-message');
115836
115837                 /*
115838                 labelsEnter
115839                     .append('span')
115840                     .attr('class', 'issue-autofix')
115841                     .each(function(d) {
115842                         if (!d.autoFix) return;
115843
115844                         d3_select(this)
115845                             .append('button')
115846                             .attr('title', t('issues.fix_one.title'))
115847                             .datum(d.autoFix)  // set button datum to the autofix
115848                             .attr('class', 'autofix action')
115849                             .on('click', function(d) {
115850                                 d3_event.preventDefault();
115851                                 d3_event.stopPropagation();
115852
115853                                 var issuesEntityIDs = d.issue.entityIds;
115854                                 utilHighlightEntities(issuesEntityIDs.concat(d.entityIds), false, context);
115855
115856                                 context.perform.apply(context, d.autoArgs);
115857                                 context.validator().validate();
115858                             })
115859                             .call(svgIcon('#iD-icon-wrench'));
115860                     });
115861                 */
115862
115863                 // Update
115864                 items = items
115865                     .merge(itemsEnter)
115866                     .order();
115867
115868                 items.selectAll('.issue-message')
115869                     .text(function(d) {
115870                         return d.message(context);
115871                     });
115872
115873                 /*
115874                 // autofix
115875                 var canAutoFix = issues.filter(function(issue) { return issue.autoFix; });
115876
115877                 var autoFixAll = selection.selectAll('.autofix-all')
115878                     .data(canAutoFix.length ? [0] : []);
115879
115880                 // exit
115881                 autoFixAll.exit()
115882                     .remove();
115883
115884                 // enter
115885                 var autoFixAllEnter = autoFixAll.enter()
115886                     .insert('div', '.issues-list')
115887                     .attr('class', 'autofix-all');
115888
115889                 var linkEnter = autoFixAllEnter
115890                     .append('a')
115891                     .attr('class', 'autofix-all-link')
115892                     .attr('href', '#');
115893
115894                 linkEnter
115895                     .append('span')
115896                     .attr('class', 'autofix-all-link-text')
115897                     .text(t('issues.fix_all.title'));
115898
115899                 linkEnter
115900                     .append('span')
115901                     .attr('class', 'autofix-all-link-icon')
115902                     .call(svgIcon('#iD-icon-wrench'));
115903
115904                 if (severity === 'warning') {
115905                     renderIgnoredIssuesReset(selection);
115906                 }
115907
115908                 // update
115909                 autoFixAll = autoFixAll
115910                     .merge(autoFixAllEnter);
115911
115912                 autoFixAll.selectAll('.autofix-all-link')
115913                     .on('click', function() {
115914                         context.pauseChangeDispatch();
115915                         context.perform(actionNoop());
115916                         canAutoFix.forEach(function(issue) {
115917                             var args = issue.autoFix.autoArgs.slice();  // copy
115918                             if (typeof args[args.length - 1] !== 'function') {
115919                                 args.pop();
115920                             }
115921                             args.push(t('issues.fix_all.annotation'));
115922                             context.replace.apply(context, args);
115923                         });
115924                         context.resumeChangeDispatch();
115925                         context.validator().validate();
115926                     });
115927                 */
115928             }
115929
115930             context.validator().on('validated.uiSectionValidationIssues' + id, function() {
115931                 window.requestIdleCallback(function() {
115932                     reloadIssues();
115933                     section.reRender();
115934                 });
115935             });
115936
115937             context.map().on('move.uiSectionValidationIssues' + id,
115938                 debounce(function() {
115939                     window.requestIdleCallback(function() {
115940                         if (getOptions().where === 'visible') {
115941                             // must refetch issues if they are viewport-dependent
115942                             reloadIssues();
115943                         }
115944                         // always reload list to re-sort-by-distance
115945                         section.reRender();
115946                     });
115947                 }, 1000)
115948             );
115949
115950             return section;
115951         }
115952
115953         function uiSectionValidationOptions(context) {
115954
115955             var section = uiSection('issues-options', context)
115956                 .content(renderContent);
115957
115958             function renderContent(selection) {
115959
115960                 var container = selection.selectAll('.issues-options-container')
115961                     .data([0]);
115962
115963                 container = container.enter()
115964                     .append('div')
115965                     .attr('class', 'issues-options-container')
115966                     .merge(container);
115967
115968                 var data = [
115969                     { key: 'what', values: ['edited', 'all'] },
115970                     { key: 'where', values: ['visible', 'all'] }
115971                 ];
115972
115973                 var options = container.selectAll('.issues-option')
115974                     .data(data, function(d) { return d.key; });
115975
115976                 var optionsEnter = options.enter()
115977                     .append('div')
115978                     .attr('class', function(d) { return 'issues-option issues-option-' + d.key; });
115979
115980                 optionsEnter
115981                     .append('div')
115982                     .attr('class', 'issues-option-title')
115983                     .text(function(d) { return _t('issues.options.' + d.key + '.title'); });
115984
115985                 var valuesEnter = optionsEnter.selectAll('label')
115986                     .data(function(d) {
115987                         return d.values.map(function(val) { return { value: val, key: d.key }; });
115988                     })
115989                     .enter()
115990                     .append('label');
115991
115992                 valuesEnter
115993                     .append('input')
115994                     .attr('type', 'radio')
115995                     .attr('name', function(d) { return 'issues-option-' + d.key; })
115996                     .attr('value', function(d) { return d.value; })
115997                     .property('checked', function(d) { return getOptions()[d.key] === d.value; })
115998                     .on('change', function(d) { updateOptionValue(d.key, d.value); });
115999
116000                 valuesEnter
116001                     .append('span')
116002                     .text(function(d) { return _t('issues.options.' + d.key + '.' + d.value); });
116003             }
116004
116005             function getOptions() {
116006                 return {
116007                     what: corePreferences('validate-what') || 'edited',  // 'all', 'edited'
116008                     where: corePreferences('validate-where') || 'all'    // 'all', 'visible'
116009                 };
116010             }
116011
116012             function updateOptionValue(d, val) {
116013                 if (!val && event && event.target) {
116014                     val = event.target.value;
116015                 }
116016
116017                 corePreferences('validate-' + d, val);
116018                 context.validator().validate();
116019             }
116020
116021             return section;
116022         }
116023
116024         function uiSectionValidationRules(context) {
116025
116026             var MINSQUARE = 0;
116027             var MAXSQUARE = 20;
116028             var DEFAULTSQUARE = 5;  // see also unsquare_way.js
116029
116030             var section = uiSection('issues-rules', context)
116031                 .disclosureContent(renderDisclosureContent)
116032                 .title(_t('issues.rules.title'));
116033
116034             var _ruleKeys = context.validator().getRuleKeys()
116035                 .filter(function(key) { return key !== 'maprules'; })
116036                 .sort(function(key1, key2) {
116037                     // alphabetize by localized title
116038                     return _t('issues.' + key1 + '.title') < _t('issues.' + key2 + '.title') ? -1 : 1;
116039                 });
116040
116041             function renderDisclosureContent(selection) {
116042                 var container = selection.selectAll('.issues-rulelist-container')
116043                     .data([0]);
116044
116045                 var containerEnter = container.enter()
116046                     .append('div')
116047                     .attr('class', 'issues-rulelist-container');
116048
116049                 containerEnter
116050                     .append('ul')
116051                     .attr('class', 'layer-list issue-rules-list');
116052
116053                 var ruleLinks = containerEnter
116054                     .append('div')
116055                     .attr('class', 'issue-rules-links section-footer');
116056
116057                 ruleLinks
116058                     .append('a')
116059                     .attr('class', 'issue-rules-link')
116060                     .attr('href', '#')
116061                     .text(_t('issues.enable_all'))
116062                     .on('click', function() {
116063                         context.validator().disableRules([]);
116064                     });
116065
116066                 ruleLinks
116067                     .append('a')
116068                     .attr('class', 'issue-rules-link')
116069                     .attr('href', '#')
116070                     .text(_t('issues.disable_all'))
116071                     .on('click', function() {
116072                         context.validator().disableRules(_ruleKeys);
116073                     });
116074
116075
116076                 // Update
116077                 container = container
116078                     .merge(containerEnter);
116079
116080                 container.selectAll('.issue-rules-list')
116081                     .call(drawListItems, _ruleKeys, 'checkbox', 'rule', toggleRule, isRuleEnabled);
116082             }
116083
116084             function drawListItems(selection, data, type, name, change, active) {
116085                 var items = selection.selectAll('li')
116086                     .data(data);
116087
116088                 // Exit
116089                 items.exit()
116090                     .remove();
116091
116092                 // Enter
116093                 var enter = items.enter()
116094                     .append('li');
116095
116096                 if (name === 'rule') {
116097                     enter
116098                         .call(uiTooltip()
116099                             .title(function(d) { return _t('issues.' + d + '.tip'); })
116100                             .placement('top')
116101                         );
116102                 }
116103
116104                 var label = enter
116105                     .append('label');
116106
116107                 label
116108                     .append('input')
116109                     .attr('type', type)
116110                     .attr('name', name)
116111                     .on('change', change);
116112
116113                 label
116114                     .append('span')
116115                     .html(function(d) {
116116                         var params = {};
116117                         if (d === 'unsquare_way') {
116118                             params.val = '<span class="square-degrees"></span>';
116119                         }
116120                         return _t('issues.' + d + '.title', params);
116121                     });
116122
116123                 // Update
116124                 items = items
116125                     .merge(enter);
116126
116127                 items
116128                     .classed('active', active)
116129                     .selectAll('input')
116130                     .property('checked', active)
116131                     .property('indeterminate', false);
116132
116133
116134                 // user-configurable square threshold
116135                 var degStr = corePreferences('validate-square-degrees');
116136                 if (degStr === null) {
116137                     degStr = '' + DEFAULTSQUARE;
116138                 }
116139
116140                 var span = items.selectAll('.square-degrees');
116141                 var input = span.selectAll('.square-degrees-input')
116142                     .data([0]);
116143
116144                 // enter / update
116145                 input.enter()
116146                     .append('input')
116147                     .attr('type', 'number')
116148                     .attr('min', '' + MINSQUARE)
116149                     .attr('max', '' + MAXSQUARE)
116150                     .attr('step', '0.5')
116151                     .attr('class', 'square-degrees-input')
116152                     .call(utilNoAuto)
116153                     .on('click', function () {
116154                         event.preventDefault();
116155                         event.stopPropagation();
116156                         this.select();
116157                     })
116158                     .on('keyup', function () {
116159                         if (event.keyCode === 13) { // enter
116160                             this.blur();
116161                             this.select();
116162                         }
116163                     })
116164                     .on('blur', changeSquare)
116165                     .merge(input)
116166                     .property('value', degStr);
116167             }
116168
116169             function changeSquare() {
116170                 var input = select(this);
116171                 var degStr = utilGetSetValue(input).trim();
116172                 var degNum = parseFloat(degStr, 10);
116173
116174                 if (!isFinite(degNum)) {
116175                     degNum = DEFAULTSQUARE;
116176                 } else if (degNum > MAXSQUARE) {
116177                     degNum = MAXSQUARE;
116178                 } else if (degNum < MINSQUARE) {
116179                     degNum = MINSQUARE;
116180                 }
116181
116182                 degNum = Math.round(degNum * 10 ) / 10;   // round to 1 decimal
116183                 degStr = '' + degNum;
116184
116185                 input
116186                     .property('value', degStr);
116187
116188                 corePreferences('validate-square-degrees', degStr);
116189                 context.validator().reloadUnsquareIssues();
116190             }
116191
116192             function isRuleEnabled(d) {
116193                 return context.validator().isRuleEnabled(d);
116194             }
116195
116196             function toggleRule(d) {
116197                 context.validator().toggleRule(d);
116198             }
116199
116200             context.validator().on('validated.uiSectionValidationRules', function() {
116201                 window.requestIdleCallback(section.reRender);
116202             });
116203
116204             return section;
116205         }
116206
116207         function uiSectionValidationStatus(context) {
116208
116209             var section = uiSection('issues-status', context)
116210                 .content(renderContent)
116211                 .shouldDisplay(function() {
116212                     var issues = context.validator().getIssues(getOptions());
116213                     return issues.length === 0;
116214                 });
116215
116216             function getOptions() {
116217                 return {
116218                     what: corePreferences('validate-what') || 'edited',
116219                     where: corePreferences('validate-where') || 'all'
116220                 };
116221             }
116222
116223             function renderContent(selection) {
116224
116225                 var box = selection.selectAll('.box')
116226                     .data([0]);
116227
116228                 var boxEnter = box.enter()
116229                     .append('div')
116230                     .attr('class', 'box');
116231
116232                 boxEnter
116233                     .append('div')
116234                     .call(svgIcon('#iD-icon-apply', 'pre-text'));
116235
116236                 var noIssuesMessage = boxEnter
116237                     .append('span');
116238
116239                 noIssuesMessage
116240                     .append('strong')
116241                     .attr('class', 'message');
116242
116243                 noIssuesMessage
116244                     .append('br');
116245
116246                 noIssuesMessage
116247                     .append('span')
116248                     .attr('class', 'details');
116249
116250                 renderIgnoredIssuesReset(selection);
116251                 setNoIssuesText(selection);
116252             }
116253
116254             function renderIgnoredIssuesReset(selection) {
116255
116256                 var ignoredIssues = context.validator()
116257                     .getIssues({ what: 'all', where: 'all', includeDisabledRules: true, includeIgnored: 'only' });
116258
116259                 var resetIgnored = selection.selectAll('.reset-ignored')
116260                     .data(ignoredIssues.length ? [0] : []);
116261
116262                 // exit
116263                 resetIgnored.exit()
116264                     .remove();
116265
116266                 // enter
116267                 var resetIgnoredEnter = resetIgnored.enter()
116268                     .append('div')
116269                     .attr('class', 'reset-ignored section-footer');
116270
116271                 resetIgnoredEnter
116272                     .append('a')
116273                     .attr('href', '#');
116274
116275                 // update
116276                 resetIgnored = resetIgnored
116277                     .merge(resetIgnoredEnter);
116278
116279                 resetIgnored.select('a')
116280                     .text(_t('issues.reset_ignored', { count: ignoredIssues.length.toString() }));
116281
116282                 resetIgnored.on('click', function() {
116283                     context.validator().resetIgnoredIssues();
116284                 });
116285             }
116286
116287             function setNoIssuesText(selection) {
116288
116289                 var opts = getOptions();
116290
116291                 function checkForHiddenIssues(cases) {
116292                     for (var type in cases) {
116293                         var hiddenOpts = cases[type];
116294                         var hiddenIssues = context.validator().getIssues(hiddenOpts);
116295                         if (hiddenIssues.length) {
116296                             selection.select('.box .details')
116297                                 .text(_t(
116298                                     'issues.no_issues.hidden_issues.' + type,
116299                                     { count: hiddenIssues.length.toString() }
116300                                 ));
116301                             return;
116302                         }
116303                     }
116304                     selection.select('.box .details')
116305                         .text(_t('issues.no_issues.hidden_issues.none'));
116306                 }
116307
116308                 var messageType;
116309
116310                 if (opts.what === 'edited' && opts.where === 'visible') {
116311
116312                     messageType = 'edits_in_view';
116313
116314                     checkForHiddenIssues({
116315                         elsewhere: { what: 'edited', where: 'all' },
116316                         everything_else: { what: 'all', where: 'visible' },
116317                         disabled_rules: { what: 'edited', where: 'visible', includeDisabledRules: 'only' },
116318                         everything_else_elsewhere: { what: 'all', where: 'all' },
116319                         disabled_rules_elsewhere: { what: 'edited', where: 'all', includeDisabledRules: 'only' },
116320                         ignored_issues: { what: 'edited', where: 'visible', includeIgnored: 'only' },
116321                         ignored_issues_elsewhere: { what: 'edited', where: 'all', includeIgnored: 'only' }
116322                     });
116323
116324                 } else if (opts.what === 'edited' && opts.where === 'all') {
116325
116326                     messageType = 'edits';
116327
116328                     checkForHiddenIssues({
116329                         everything_else: { what: 'all', where: 'all' },
116330                         disabled_rules: { what: 'edited', where: 'all', includeDisabledRules: 'only' },
116331                         ignored_issues: { what: 'edited', where: 'all', includeIgnored: 'only' }
116332                     });
116333
116334                 } else if (opts.what === 'all' && opts.where === 'visible') {
116335
116336                     messageType = 'everything_in_view';
116337
116338                     checkForHiddenIssues({
116339                         elsewhere: { what: 'all', where: 'all' },
116340                         disabled_rules: { what: 'all', where: 'visible', includeDisabledRules: 'only' },
116341                         disabled_rules_elsewhere: { what: 'all', where: 'all', includeDisabledRules: 'only' },
116342                         ignored_issues: { what: 'all', where: 'visible', includeIgnored: 'only' },
116343                         ignored_issues_elsewhere: { what: 'all', where: 'all', includeIgnored: 'only' }
116344                     });
116345                 } else if (opts.what === 'all' && opts.where === 'all') {
116346
116347                     messageType = 'everything';
116348
116349                     checkForHiddenIssues({
116350                         disabled_rules: { what: 'all', where: 'all', includeDisabledRules: 'only' },
116351                         ignored_issues: { what: 'all', where: 'all', includeIgnored: 'only' }
116352                     });
116353                 }
116354
116355                 if (opts.what === 'edited' && context.history().difference().summary().length === 0) {
116356                     messageType = 'no_edits';
116357                 }
116358
116359                 selection.select('.box .message')
116360                     .text(_t('issues.no_issues.message.' + messageType));
116361
116362             }
116363
116364             context.validator().on('validated.uiSectionValidationStatus', function() {
116365                 window.requestIdleCallback(section.reRender);
116366             });
116367
116368             context.map().on('move.uiSectionValidationStatus',
116369                 debounce(function() {
116370                     window.requestIdleCallback(section.reRender);
116371                 }, 1000)
116372             );
116373
116374             return section;
116375         }
116376
116377         function uiPaneIssues(context) {
116378
116379             var issuesPane = uiPane('issues', context)
116380                 .key(_t('issues.key'))
116381                 .title(_t('issues.title'))
116382                 .description(_t('issues.title'))
116383                 .iconName('iD-icon-alert')
116384                 .sections([
116385                     uiSectionValidationOptions(context),
116386                     uiSectionValidationStatus(context),
116387                     uiSectionValidationIssues('issues-errors', 'error', context),
116388                     uiSectionValidationIssues('issues-warnings', 'warning', context),
116389                     uiSectionValidationRules(context)
116390                 ]);
116391
116392             return issuesPane;
116393         }
116394
116395         function uiSettingsCustomData(context) {
116396             var dispatch$1 = dispatch('change');
116397
116398             function render(selection) {
116399                 var dataLayer = context.layers().layer('data');
116400
116401                 // keep separate copies of original and current settings
116402                 var _origSettings = {
116403                     fileList: (dataLayer && dataLayer.fileList()) || null,
116404                     url: corePreferences('settings-custom-data-url')
116405                 };
116406                 var _currSettings = {
116407                     fileList: (dataLayer && dataLayer.fileList()) || null,
116408                     url: corePreferences('settings-custom-data-url')
116409                 };
116410
116411                 // var example = 'https://{switch:a,b,c}.tile.openstreetmap.org/{zoom}/{x}/{y}.png';
116412                 var modal = uiConfirm(selection).okButton();
116413
116414                 modal
116415                     .classed('settings-modal settings-custom-data', true);
116416
116417                 modal.select('.modal-section.header')
116418                     .append('h3')
116419                     .text(_t('settings.custom_data.header'));
116420
116421
116422                 var textSection = modal.select('.modal-section.message-text');
116423
116424                 textSection
116425                     .append('pre')
116426                     .attr('class', 'instructions-file')
116427                     .text(_t('settings.custom_data.file.instructions'));
116428
116429                 textSection
116430                     .append('input')
116431                     .attr('class', 'field-file')
116432                     .attr('type', 'file')
116433                     .property('files', _currSettings.fileList)  // works for all except IE11
116434                     .on('change', function() {
116435                         var files = event.target.files;
116436                         if (files && files.length) {
116437                             _currSettings.url = '';
116438                             textSection.select('.field-url').property('value', '');
116439                             _currSettings.fileList = files;
116440                         } else {
116441                             _currSettings.fileList = null;
116442                         }
116443                     });
116444
116445                 textSection
116446                     .append('h4')
116447                     .text(_t('settings.custom_data.or'));
116448
116449                 textSection
116450                     .append('pre')
116451                     .attr('class', 'instructions-url')
116452                     .text(_t('settings.custom_data.url.instructions'));
116453
116454                 textSection
116455                     .append('textarea')
116456                     .attr('class', 'field-url')
116457                     .attr('placeholder', _t('settings.custom_data.url.placeholder'))
116458                     .call(utilNoAuto)
116459                     .property('value', _currSettings.url);
116460
116461
116462                 // insert a cancel button
116463                 var buttonSection = modal.select('.modal-section.buttons');
116464
116465                 buttonSection
116466                     .insert('button', '.ok-button')
116467                     .attr('class', 'button cancel-button secondary-action')
116468                     .text(_t('confirm.cancel'));
116469
116470
116471                 buttonSection.select('.cancel-button')
116472                     .on('click.cancel', clickCancel);
116473
116474                 buttonSection.select('.ok-button')
116475                     .attr('disabled', isSaveDisabled)
116476                     .on('click.save', clickSave);
116477
116478
116479                 function isSaveDisabled() {
116480                     return null;
116481                 }
116482
116483
116484                 // restore the original url
116485                 function clickCancel() {
116486                     textSection.select('.field-url').property('value', _origSettings.url);
116487                     corePreferences('settings-custom-data-url', _origSettings.url);
116488                     this.blur();
116489                     modal.close();
116490                 }
116491
116492                 // accept the current url
116493                 function clickSave() {
116494                     _currSettings.url = textSection.select('.field-url').property('value').trim();
116495
116496                     // one or the other but not both
116497                     if (_currSettings.url) { _currSettings.fileList = null; }
116498                     if (_currSettings.fileList) { _currSettings.url = ''; }
116499
116500                     corePreferences('settings-custom-data-url', _currSettings.url);
116501                     this.blur();
116502                     modal.close();
116503                     dispatch$1.call('change', this, _currSettings);
116504                 }
116505             }
116506
116507             return utilRebind(render, dispatch$1, 'on');
116508         }
116509
116510         function uiSectionDataLayers(context) {
116511
116512             var settingsCustomData = uiSettingsCustomData(context)
116513                 .on('change', customChanged);
116514
116515             var layers = context.layers();
116516
116517             var section = uiSection('data-layers', context)
116518                 .title(_t('map_data.data_layers'))
116519                 .disclosureContent(renderDisclosureContent);
116520
116521             function renderDisclosureContent(selection) {
116522                 var container = selection.selectAll('.data-layer-container')
116523                     .data([0]);
116524
116525                 container.enter()
116526                     .append('div')
116527                     .attr('class', 'data-layer-container')
116528                     .merge(container)
116529                     .call(drawOsmItems)
116530                     .call(drawQAItems)
116531                     .call(drawCustomDataItems)
116532                     .call(drawVectorItems)      // Beta - Detroit mapping challenge
116533                     .call(drawPanelItems);
116534             }
116535
116536             function showsLayer(which) {
116537                 var layer = layers.layer(which);
116538                 if (layer) {
116539                     return layer.enabled();
116540                 }
116541                 return false;
116542             }
116543
116544             function setLayer(which, enabled) {
116545                 // Don't allow layer changes while drawing - #6584
116546                 var mode = context.mode();
116547                 if (mode && /^draw/.test(mode.id)) { return; }
116548
116549                 var layer = layers.layer(which);
116550                 if (layer) {
116551                     layer.enabled(enabled);
116552
116553                     if (!enabled && (which === 'osm' || which === 'notes')) {
116554                         context.enter(modeBrowse(context));
116555                     }
116556                 }
116557             }
116558
116559             function toggleLayer(which) {
116560                 setLayer(which, !showsLayer(which));
116561             }
116562
116563             function drawOsmItems(selection) {
116564                 var osmKeys = ['osm', 'notes'];
116565                 var osmLayers = layers.all().filter(function(obj) { return osmKeys.indexOf(obj.id) !== -1; });
116566
116567                 var ul = selection
116568                     .selectAll('.layer-list-osm')
116569                     .data([0]);
116570
116571                 ul = ul.enter()
116572                     .append('ul')
116573                     .attr('class', 'layer-list layer-list-osm')
116574                     .merge(ul);
116575
116576                 var li = ul.selectAll('.list-item')
116577                     .data(osmLayers);
116578
116579                 li.exit()
116580                     .remove();
116581
116582                 var liEnter = li.enter()
116583                     .append('li')
116584                     .attr('class', function(d) { return 'list-item list-item-' + d.id; });
116585
116586                 var labelEnter = liEnter
116587                     .append('label')
116588                     .each(function(d) {
116589                         if (d.id === 'osm') {
116590                             select(this)
116591                                 .call(uiTooltip()
116592                                     .title(_t('map_data.layers.' + d.id + '.tooltip'))
116593                                     .keys([uiCmd('⌥' + _t('area_fill.wireframe.key'))])
116594                                     .placement('bottom')
116595                                 );
116596                         } else {
116597                             select(this)
116598                                 .call(uiTooltip()
116599                                     .title(_t('map_data.layers.' + d.id + '.tooltip'))
116600                                     .placement('bottom')
116601                                 );
116602                         }
116603                     });
116604
116605                 labelEnter
116606                     .append('input')
116607                     .attr('type', 'checkbox')
116608                     .on('change', function(d) { toggleLayer(d.id); });
116609
116610                 labelEnter
116611                     .append('span')
116612                     .text(function(d) { return _t('map_data.layers.' + d.id + '.title'); });
116613
116614
116615                 // Update
116616                 li
116617                     .merge(liEnter)
116618                     .classed('active', function (d) { return d.layer.enabled(); })
116619                     .selectAll('input')
116620                     .property('checked', function (d) { return d.layer.enabled(); });
116621             }
116622
116623             function drawQAItems(selection) {
116624                 var qaKeys = ['keepRight', 'improveOSM', 'osmose'];
116625                 var qaLayers = layers.all().filter(function(obj) { return qaKeys.indexOf(obj.id) !== -1; });
116626
116627                 var ul = selection
116628                     .selectAll('.layer-list-qa')
116629                     .data([0]);
116630
116631                 ul = ul.enter()
116632                     .append('ul')
116633                     .attr('class', 'layer-list layer-list-qa')
116634                     .merge(ul);
116635
116636                 var li = ul.selectAll('.list-item')
116637                     .data(qaLayers);
116638
116639                 li.exit()
116640                     .remove();
116641
116642                 var liEnter = li.enter()
116643                     .append('li')
116644                     .attr('class', function(d) { return 'list-item list-item-' + d.id; });
116645
116646                 var labelEnter = liEnter
116647                     .append('label')
116648                     .each(function(d) {
116649                         select(this)
116650                             .call(uiTooltip()
116651                                 .title(_t('map_data.layers.' + d.id + '.tooltip'))
116652                                 .placement('bottom')
116653                             );
116654                     });
116655
116656                 labelEnter
116657                     .append('input')
116658                     .attr('type', 'checkbox')
116659                     .on('change', function(d) { toggleLayer(d.id); });
116660
116661                 labelEnter
116662                     .append('span')
116663                     .text(function(d) { return _t('map_data.layers.' + d.id + '.title'); });
116664
116665
116666                 // Update
116667                 li
116668                     .merge(liEnter)
116669                     .classed('active', function (d) { return d.layer.enabled(); })
116670                     .selectAll('input')
116671                     .property('checked', function (d) { return d.layer.enabled(); });
116672             }
116673
116674             // Beta feature - sample vector layers to support Detroit Mapping Challenge
116675             // https://github.com/osmus/detroit-mapping-challenge
116676             function drawVectorItems(selection) {
116677                 var dataLayer = layers.layer('data');
116678                 var vtData = [
116679                     {
116680                         name: 'Detroit Neighborhoods/Parks',
116681                         src: 'neighborhoods-parks',
116682                         tooltip: 'Neighborhood boundaries and parks as compiled by City of Detroit in concert with community groups.',
116683                         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'
116684                     }, {
116685                         name: 'Detroit Composite POIs',
116686                         src: 'composite-poi',
116687                         tooltip: 'Fire Inspections, Business Licenses, and other public location data collated from the City of Detroit.',
116688                         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'
116689                     }, {
116690                         name: 'Detroit All-The-Places POIs',
116691                         src: 'alltheplaces-poi',
116692                         tooltip: 'Public domain business location data created by web scrapers.',
116693                         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'
116694                     }
116695                 ];
116696
116697                 // Only show this if the map is around Detroit..
116698                 var detroit = geoExtent([-83.5, 42.1], [-82.8, 42.5]);
116699                 var showVectorItems = (context.map().zoom() > 9 && detroit.contains(context.map().center()));
116700
116701                 var container = selection.selectAll('.vectortile-container')
116702                     .data(showVectorItems ? [0] : []);
116703
116704                 container.exit()
116705                     .remove();
116706
116707                 var containerEnter = container.enter()
116708                     .append('div')
116709                     .attr('class', 'vectortile-container');
116710
116711                 containerEnter
116712                     .append('h4')
116713                     .attr('class', 'vectortile-header')
116714                     .text('Detroit Vector Tiles (Beta)');
116715
116716                 containerEnter
116717                     .append('ul')
116718                     .attr('class', 'layer-list layer-list-vectortile');
116719
116720                 containerEnter
116721                     .append('div')
116722                     .attr('class', 'vectortile-footer')
116723                     .append('a')
116724                     .attr('target', '_blank')
116725                     .attr('tabindex', -1)
116726                     .call(svgIcon('#iD-icon-out-link', 'inline'))
116727                     .attr('href', 'https://github.com/osmus/detroit-mapping-challenge')
116728                     .append('span')
116729                     .text('About these layers');
116730
116731                 container = container
116732                     .merge(containerEnter);
116733
116734
116735                 var ul = container.selectAll('.layer-list-vectortile');
116736
116737                 var li = ul.selectAll('.list-item')
116738                     .data(vtData);
116739
116740                 li.exit()
116741                     .remove();
116742
116743                 var liEnter = li.enter()
116744                     .append('li')
116745                     .attr('class', function(d) { return 'list-item list-item-' + d.src; });
116746
116747                 var labelEnter = liEnter
116748                     .append('label')
116749                     .each(function(d) {
116750                         select(this).call(
116751                             uiTooltip().title(d.tooltip).placement('top')
116752                         );
116753                     });
116754
116755                 labelEnter
116756                     .append('input')
116757                     .attr('type', 'radio')
116758                     .attr('name', 'vectortile')
116759                     .on('change', selectVTLayer);
116760
116761                 labelEnter
116762                     .append('span')
116763                     .text(function(d) { return d.name; });
116764
116765                 // Update
116766                 li
116767                     .merge(liEnter)
116768                     .classed('active', isVTLayerSelected)
116769                     .selectAll('input')
116770                     .property('checked', isVTLayerSelected);
116771
116772
116773                 function isVTLayerSelected(d) {
116774                     return dataLayer && dataLayer.template() === d.template;
116775                 }
116776
116777                 function selectVTLayer(d) {
116778                     corePreferences('settings-custom-data-url', d.template);
116779                     if (dataLayer) {
116780                         dataLayer.template(d.template, d.src);
116781                         dataLayer.enabled(true);
116782                     }
116783                 }
116784             }
116785
116786             function drawCustomDataItems(selection) {
116787                 var dataLayer = layers.layer('data');
116788                 var hasData = dataLayer && dataLayer.hasData();
116789                 var showsData = hasData && dataLayer.enabled();
116790
116791                 var ul = selection
116792                     .selectAll('.layer-list-data')
116793                     .data(dataLayer ? [0] : []);
116794
116795                 // Exit
116796                 ul.exit()
116797                     .remove();
116798
116799                 // Enter
116800                 var ulEnter = ul.enter()
116801                     .append('ul')
116802                     .attr('class', 'layer-list layer-list-data');
116803
116804                 var liEnter = ulEnter
116805                     .append('li')
116806                     .attr('class', 'list-item-data');
116807
116808                 var labelEnter = liEnter
116809                     .append('label')
116810                     .call(uiTooltip()
116811                         .title(_t('map_data.layers.custom.tooltip'))
116812                         .placement('top')
116813                     );
116814
116815                 labelEnter
116816                     .append('input')
116817                     .attr('type', 'checkbox')
116818                     .on('change', function() { toggleLayer('data'); });
116819
116820                 labelEnter
116821                     .append('span')
116822                     .text(_t('map_data.layers.custom.title'));
116823
116824                 liEnter
116825                     .append('button')
116826                     .call(uiTooltip()
116827                         .title(_t('settings.custom_data.tooltip'))
116828                         .placement((_mainLocalizer.textDirection() === 'rtl') ? 'right' : 'left')
116829                     )
116830                     .on('click', editCustom)
116831                     .call(svgIcon('#iD-icon-more'));
116832
116833                 liEnter
116834                     .append('button')
116835                     .call(uiTooltip()
116836                         .title(_t('map_data.layers.custom.zoom'))
116837                         .placement((_mainLocalizer.textDirection() === 'rtl') ? 'right' : 'left')
116838                     )
116839                     .on('click', function() {
116840                         event.preventDefault();
116841                         event.stopPropagation();
116842                         dataLayer.fitZoom();
116843                     })
116844                     .call(svgIcon('#iD-icon-framed-dot'));
116845
116846                 // Update
116847                 ul = ul
116848                     .merge(ulEnter);
116849
116850                 ul.selectAll('.list-item-data')
116851                     .classed('active', showsData)
116852                     .selectAll('label')
116853                     .classed('deemphasize', !hasData)
116854                     .selectAll('input')
116855                     .property('disabled', !hasData)
116856                     .property('checked', showsData);
116857             }
116858
116859             function editCustom() {
116860                 event.preventDefault();
116861                 context.container()
116862                     .call(settingsCustomData);
116863             }
116864
116865             function customChanged(d) {
116866                 var dataLayer = layers.layer('data');
116867
116868                 if (d && d.url) {
116869                     dataLayer.url(d.url);
116870                 } else if (d && d.fileList) {
116871                     dataLayer.fileList(d.fileList);
116872                 }
116873             }
116874
116875
116876             function drawPanelItems(selection) {
116877
116878                 var panelsListEnter = selection.selectAll('.md-extras-list')
116879                     .data([0])
116880                     .enter()
116881                     .append('ul')
116882                     .attr('class', 'layer-list md-extras-list');
116883
116884                 var historyPanelLabelEnter = panelsListEnter
116885                     .append('li')
116886                     .attr('class', 'history-panel-toggle-item')
116887                     .append('label')
116888                     .call(uiTooltip()
116889                         .title(_t('map_data.history_panel.tooltip'))
116890                         .keys([uiCmd('⌘⇧' + _t('info_panels.history.key'))])
116891                         .placement('top')
116892                     );
116893
116894                 historyPanelLabelEnter
116895                     .append('input')
116896                     .attr('type', 'checkbox')
116897                     .on('change', function() {
116898                         event.preventDefault();
116899                         context.ui().info.toggle('history');
116900                     });
116901
116902                 historyPanelLabelEnter
116903                     .append('span')
116904                     .text(_t('map_data.history_panel.title'));
116905
116906                 var measurementPanelLabelEnter = panelsListEnter
116907                     .append('li')
116908                     .attr('class', 'measurement-panel-toggle-item')
116909                     .append('label')
116910                     .call(uiTooltip()
116911                         .title(_t('map_data.measurement_panel.tooltip'))
116912                         .keys([uiCmd('⌘⇧' + _t('info_panels.measurement.key'))])
116913                         .placement('top')
116914                     );
116915
116916                 measurementPanelLabelEnter
116917                     .append('input')
116918                     .attr('type', 'checkbox')
116919                     .on('change', function() {
116920                         event.preventDefault();
116921                         context.ui().info.toggle('measurement');
116922                     });
116923
116924                 measurementPanelLabelEnter
116925                     .append('span')
116926                     .text(_t('map_data.measurement_panel.title'));
116927             }
116928
116929             context.layers().on('change.uiSectionDataLayers', section.reRender);
116930
116931             context.map()
116932                 .on('move.uiSectionDataLayers',
116933                     debounce(function() {
116934                         // Detroit layers may have moved in or out of view
116935                         window.requestIdleCallback(section.reRender);
116936                     }, 1000)
116937                 );
116938
116939             return section;
116940         }
116941
116942         function uiSectionMapFeatures(context) {
116943
116944             var _features = context.features().keys();
116945
116946             var section = uiSection('map-features', context)
116947                 .title(_t('map_data.map_features'))
116948                 .disclosureContent(renderDisclosureContent)
116949                 .expandedByDefault(false);
116950
116951             function renderDisclosureContent(selection) {
116952
116953                 var container = selection.selectAll('.layer-feature-list-container')
116954                     .data([0]);
116955
116956                 var containerEnter = container.enter()
116957                     .append('div')
116958                     .attr('class', 'layer-feature-list-container');
116959
116960                 containerEnter
116961                     .append('ul')
116962                     .attr('class', 'layer-list layer-feature-list');
116963
116964                 var footer = containerEnter
116965                     .append('div')
116966                     .attr('class', 'feature-list-links section-footer');
116967
116968                 footer
116969                     .append('a')
116970                     .attr('class', 'feature-list-link')
116971                     .attr('href', '#')
116972                     .text(_t('issues.enable_all'))
116973                     .on('click', function() {
116974                         context.features().enableAll();
116975                     });
116976
116977                 footer
116978                     .append('a')
116979                     .attr('class', 'feature-list-link')
116980                     .attr('href', '#')
116981                     .text(_t('issues.disable_all'))
116982                     .on('click', function() {
116983                         context.features().disableAll();
116984                     });
116985
116986                 // Update
116987                 container = container
116988                     .merge(containerEnter);
116989
116990                 container.selectAll('.layer-feature-list')
116991                     .call(drawListItems, _features, 'checkbox', 'feature', clickFeature, showsFeature);
116992             }
116993
116994             function drawListItems(selection, data, type, name, change, active) {
116995                 var items = selection.selectAll('li')
116996                     .data(data);
116997
116998                 // Exit
116999                 items.exit()
117000                     .remove();
117001
117002                 // Enter
117003                 var enter = items.enter()
117004                     .append('li')
117005                     .call(uiTooltip()
117006                         .title(function(d) {
117007                             var tip = _t(name + '.' + d + '.tooltip');
117008                             if (autoHiddenFeature(d)) {
117009                                 var msg = showsLayer('osm') ? _t('map_data.autohidden') : _t('map_data.osmhidden');
117010                                 tip += '<div>' + msg + '</div>';
117011                             }
117012                             return tip;
117013                         })
117014                         .placement('top')
117015                     );
117016
117017                 var label = enter
117018                     .append('label');
117019
117020                 label
117021                     .append('input')
117022                     .attr('type', type)
117023                     .attr('name', name)
117024                     .on('change', change);
117025
117026                 label
117027                     .append('span')
117028                     .text(function(d) { return _t(name + '.' + d + '.description'); });
117029
117030                 // Update
117031                 items = items
117032                     .merge(enter);
117033
117034                 items
117035                     .classed('active', active)
117036                     .selectAll('input')
117037                     .property('checked', active)
117038                     .property('indeterminate', autoHiddenFeature);
117039             }
117040
117041             function autoHiddenFeature(d) {
117042                 return context.features().autoHidden(d);
117043             }
117044
117045             function showsFeature(d) {
117046                 return context.features().enabled(d);
117047             }
117048
117049             function clickFeature(d) {
117050                 context.features().toggle(d);
117051             }
117052
117053             function showsLayer(id) {
117054                 var layer = context.layers().layer(id);
117055                 return layer && layer.enabled();
117056             }
117057
117058             // add listeners
117059             context.features()
117060                 .on('change.map_features', section.reRender);
117061
117062             return section;
117063         }
117064
117065         function uiSectionMapStyleOptions(context) {
117066
117067             var section = uiSection('fill-area', context)
117068                 .title(_t('map_data.style_options'))
117069                 .disclosureContent(renderDisclosureContent)
117070                 .expandedByDefault(false);
117071
117072             function renderDisclosureContent(selection) {
117073                 var container = selection.selectAll('.layer-fill-list')
117074                     .data([0]);
117075
117076                 container.enter()
117077                     .append('ul')
117078                     .attr('class', 'layer-list layer-fill-list')
117079                     .merge(container)
117080                     .call(drawListItems, context.map().areaFillOptions, 'radio', 'area_fill', setFill, isActiveFill);
117081
117082                 var container2 = selection.selectAll('.layer-visual-diff-list')
117083                     .data([0]);
117084
117085                 container2.enter()
117086                     .append('ul')
117087                     .attr('class', 'layer-list layer-visual-diff-list')
117088                     .merge(container2)
117089                     .call(drawListItems, ['highlight_edits'], 'checkbox', 'visual_diff', toggleHighlightEdited, function() {
117090                         return context.surface().classed('highlight-edited');
117091                     });
117092             }
117093
117094             function drawListItems(selection, data, type, name, change, active) {
117095                 var items = selection.selectAll('li')
117096                     .data(data);
117097
117098                 // Exit
117099                 items.exit()
117100                     .remove();
117101
117102                 // Enter
117103                 var enter = items.enter()
117104                     .append('li')
117105                     .call(uiTooltip()
117106                         .title(function(d) {
117107                             return _t(name + '.' + d + '.tooltip');
117108                         })
117109                         .keys(function(d) {
117110                             var key = (d === 'wireframe' ? _t('area_fill.wireframe.key') : null);
117111                             if (d === 'highlight_edits') { key = _t('map_data.highlight_edits.key'); }
117112                             return key ? [key] : null;
117113                         })
117114                         .placement('top')
117115                     );
117116
117117                 var label = enter
117118                     .append('label');
117119
117120                 label
117121                     .append('input')
117122                     .attr('type', type)
117123                     .attr('name', name)
117124                     .on('change', change);
117125
117126                 label
117127                     .append('span')
117128                     .text(function(d) { return _t(name + '.' + d + '.description'); });
117129
117130                 // Update
117131                 items = items
117132                     .merge(enter);
117133
117134                 items
117135                     .classed('active', active)
117136                     .selectAll('input')
117137                     .property('checked', active)
117138                     .property('indeterminate', false);
117139             }
117140
117141             function isActiveFill(d) {
117142                 return context.map().activeAreaFill() === d;
117143             }
117144
117145             function toggleHighlightEdited() {
117146                 event.preventDefault();
117147                 context.map().toggleHighlightEdited();
117148             }
117149
117150             function setFill(d) {
117151                 context.map().activeAreaFill(d);
117152             }
117153
117154             context.map()
117155                 .on('changeHighlighting.ui_style, changeAreaFill.ui_style', section.reRender);
117156
117157             return section;
117158         }
117159
117160         function uiSectionPhotoOverlays(context) {
117161
117162             var layers = context.layers();
117163
117164             var section = uiSection('photo-overlays', context)
117165                 .title(_t('photo_overlays.title'))
117166                 .disclosureContent(renderDisclosureContent)
117167                 .expandedByDefault(false);
117168
117169             function renderDisclosureContent(selection) {
117170                 var container = selection.selectAll('.photo-overlay-container')
117171                     .data([0]);
117172
117173                 container.enter()
117174                     .append('div')
117175                     .attr('class', 'photo-overlay-container')
117176                     .merge(container)
117177                     .call(drawPhotoItems)
117178                     .call(drawPhotoTypeItems);
117179             }
117180
117181             function drawPhotoItems(selection) {
117182                 var photoKeys = context.photos().overlayLayerIDs();
117183                 var photoLayers = layers.all().filter(function(obj) { return photoKeys.indexOf(obj.id) !== -1; });
117184                 var data = photoLayers.filter(function(obj) { return obj.layer.supported(); });
117185
117186                 function layerSupported(d) {
117187                     return d.layer && d.layer.supported();
117188                 }
117189                 function layerEnabled(d) {
117190                     return layerSupported(d) && d.layer.enabled();
117191                 }
117192
117193                 var ul = selection
117194                     .selectAll('.layer-list-photos')
117195                     .data([0]);
117196
117197                 ul = ul.enter()
117198                     .append('ul')
117199                     .attr('class', 'layer-list layer-list-photos')
117200                     .merge(ul);
117201
117202                 var li = ul.selectAll('.list-item-photos')
117203                     .data(data);
117204
117205                 li.exit()
117206                     .remove();
117207
117208                 var liEnter = li.enter()
117209                     .append('li')
117210                     .attr('class', function(d) {
117211                         var classes = 'list-item-photos list-item-' + d.id;
117212                         if (d.id === 'mapillary-signs' || d.id === 'mapillary-map-features') {
117213                             classes += ' indented';
117214                         }
117215                         return classes;
117216                     });
117217
117218                 var labelEnter = liEnter
117219                     .append('label')
117220                     .each(function(d) {
117221                         var titleID;
117222                         if (d.id === 'mapillary-signs') { titleID = 'mapillary.signs.tooltip'; }
117223                         else if (d.id === 'mapillary') { titleID = 'mapillary_images.tooltip'; }
117224                         else if (d.id === 'openstreetcam') { titleID = 'openstreetcam_images.tooltip'; }
117225                         else { titleID = d.id.replace(/-/g, '_') + '.tooltip'; }
117226                         select(this)
117227                             .call(uiTooltip()
117228                                 .title(_t(titleID))
117229                                 .placement('top')
117230                             );
117231                     });
117232
117233                 labelEnter
117234                     .append('input')
117235                     .attr('type', 'checkbox')
117236                     .on('change', function(d) { toggleLayer(d.id); });
117237
117238                 labelEnter
117239                     .append('span')
117240                     .text(function(d) {
117241                         var id = d.id;
117242                         if (id === 'mapillary-signs') { id = 'photo_overlays.traffic_signs'; }
117243                         return _t(id.replace(/-/g, '_') + '.title');
117244                     });
117245
117246
117247                 // Update
117248                 li
117249                     .merge(liEnter)
117250                     .classed('active', layerEnabled)
117251                     .selectAll('input')
117252                     .property('checked', layerEnabled);
117253             }
117254
117255             function drawPhotoTypeItems(selection) {
117256                 var data = context.photos().allPhotoTypes();
117257
117258                 function typeEnabled(d) {
117259                     return context.photos().showsPhotoType(d);
117260                 }
117261
117262                 var ul = selection
117263                     .selectAll('.layer-list-photo-types')
117264                     .data(context.photos().shouldFilterByPhotoType() ? [0] : []);
117265
117266                 ul.exit()
117267                     .remove();
117268
117269                 ul = ul.enter()
117270                     .append('ul')
117271                     .attr('class', 'layer-list layer-list-photo-types')
117272                     .merge(ul);
117273
117274                 var li = ul.selectAll('.list-item-photo-types')
117275                     .data(data);
117276
117277                 li.exit()
117278                     .remove();
117279
117280                 var liEnter = li.enter()
117281                     .append('li')
117282                     .attr('class', function(d) {
117283                         return 'list-item-photo-types list-item-' + d;
117284                     });
117285
117286                 var labelEnter = liEnter
117287                     .append('label')
117288                     .each(function(d) {
117289                         select(this)
117290                             .call(uiTooltip()
117291                                 .title(_t('photo_overlays.photo_type.' + d + '.tooltip'))
117292                                 .placement('top')
117293                             );
117294                     });
117295
117296                 labelEnter
117297                     .append('input')
117298                     .attr('type', 'checkbox')
117299                     .on('change', function(d) {
117300                         context.photos().togglePhotoType(d);
117301                     });
117302
117303                 labelEnter
117304                     .append('span')
117305                     .text(function(d) {
117306                         return _t('photo_overlays.photo_type.' + d + '.title');
117307                     });
117308
117309
117310                 // Update
117311                 li
117312                     .merge(liEnter)
117313                     .classed('active', typeEnabled)
117314                     .selectAll('input')
117315                     .property('checked', typeEnabled);
117316             }
117317
117318             function toggleLayer(which) {
117319                 setLayer(which, !showsLayer(which));
117320             }
117321
117322             function showsLayer(which) {
117323                 var layer = layers.layer(which);
117324                 if (layer) {
117325                     return layer.enabled();
117326                 }
117327                 return false;
117328             }
117329
117330             function setLayer(which, enabled) {
117331                 var layer = layers.layer(which);
117332                 if (layer) {
117333                     layer.enabled(enabled);
117334                 }
117335             }
117336
117337             context.layers().on('change.uiSectionPhotoOverlays', section.reRender);
117338             context.photos().on('change.uiSectionPhotoOverlays', section.reRender);
117339
117340             return section;
117341         }
117342
117343         function uiPaneMapData(context) {
117344
117345             var mapDataPane = uiPane('map-data', context)
117346                 .key(_t('map_data.key'))
117347                 .title(_t('map_data.title'))
117348                 .description(_t('map_data.description'))
117349                 .iconName('iD-icon-data')
117350                 .sections([
117351                     uiSectionDataLayers(context),
117352                     uiSectionPhotoOverlays(context),
117353                     uiSectionMapStyleOptions(context),
117354                     uiSectionMapFeatures(context)
117355                 ]);
117356
117357             return mapDataPane;
117358         }
117359
117360         function uiSectionPrivacy(context) {
117361
117362             var section = uiSection('preferences-third-party', context)
117363               .title(_t('preferences.privacy.title'))
117364               .disclosureContent(renderDisclosureContent);
117365
117366             var _showThirdPartyIcons = corePreferences('preferences.privacy.thirdpartyicons') || 'true';
117367
117368             function renderDisclosureContent(selection) {
117369               // enter
117370               var privacyOptionsListEnter = selection.selectAll('.privacy-options-list')
117371                 .data([0])
117372                 .enter()
117373                 .append('ul')
117374                 .attr('class', 'layer-list privacy-options-list');
117375
117376               var thirdPartyIconsEnter = privacyOptionsListEnter
117377                 .append('li')
117378                 .attr('class', 'privacy-third-party-icons-item')
117379                 .append('label')
117380                 .call(uiTooltip()
117381                   .title(_t('preferences.privacy.third_party_icons.tooltip'))
117382                   .placement('bottom')
117383                 );
117384
117385               thirdPartyIconsEnter
117386                 .append('input')
117387                 .attr('type', 'checkbox')
117388                 .on('change', function () {
117389                   event.preventDefault();
117390                   _showThirdPartyIcons = (_showThirdPartyIcons === 'true') ? 'false' : 'true';
117391                   corePreferences('preferences.privacy.thirdpartyicons', _showThirdPartyIcons);
117392                   update();
117393                 });
117394
117395               thirdPartyIconsEnter
117396                 .append('span')
117397                 .text(_t('preferences.privacy.third_party_icons.description'));
117398
117399
117400               // Privacy Policy link
117401               selection.selectAll('.privacy-link')
117402                 .data([0])
117403                 .enter()
117404                 .append('div')
117405                 .attr('class', 'privacy-link')
117406                 .append('a')
117407                 .attr('target', '_blank')
117408                 .call(svgIcon('#iD-icon-out-link', 'inline'))
117409                 .attr('href', 'https://github.com/openstreetmap/iD/blob/release/PRIVACY.md')
117410                 .append('span')
117411                 .text(_t('preferences.privacy.privacy_link'));
117412
117413               update();
117414
117415
117416               function update() {
117417                 selection.selectAll('.privacy-third-party-icons-item')
117418                   .classed('active', (_showThirdPartyIcons === 'true'))
117419                   .select('input')
117420                   .property('checked', (_showThirdPartyIcons === 'true'));
117421               }
117422             }
117423
117424             return section;
117425         }
117426
117427         function uiPanePreferences(context) {
117428
117429           var preferencesPane = uiPane('preferences', context)
117430             .key(_t('preferences.key'))
117431             .title(_t('preferences.title'))
117432             .description(_t('preferences.description'))
117433             .iconName('fas-user-cog')
117434             .sections([
117435                 uiSectionPrivacy(context)
117436             ]);
117437
117438           return preferencesPane;
117439         }
117440
117441         function uiInit(context) {
117442             var _initCounter = 0;
117443             var _needWidth = {};
117444
117445             var _lastPointerType;
117446
117447
117448             function render(container) {
117449
117450                 container
117451                     .on('click.ui', function() {
117452                         // we're only concerned with the primary mouse button
117453                         if (event.button !== 0) { return; }
117454
117455                         if (!event.composedPath) { return; }
117456
117457                         // some targets have default click events we don't want to override
117458                         var isOkayTarget = event.composedPath().some(function(node) {
117459                             // we only care about element nodes
117460                             return node.nodeType === 1 &&
117461                                 // clicking <input> focuses it and/or changes a value
117462                                 (node.nodeName === 'INPUT' ||
117463                                 // clicking <label> affects its <input> by default
117464                                 node.nodeName === 'LABEL' ||
117465                                 // clicking <a> opens a hyperlink by default
117466                                 node.nodeName === 'A');
117467                         });
117468                         if (isOkayTarget) { return; }
117469
117470                         // disable double-tap-to-zoom on touchscreens
117471                         event.preventDefault();
117472                     });
117473
117474                 var detected = utilDetect();
117475
117476                 // only WebKit supports gesture events
117477                 if ('GestureEvent' in window &&
117478                     // Listening for gesture events on iOS 13.4+ breaks double-tapping,
117479                     // but we only need to do this on desktop Safari anyway. – #7694
117480                     !detected.isMobileWebKit) {
117481
117482                     // On iOS we disable pinch-to-zoom of the UI via the `touch-action`
117483                     // CSS property, but on desktop Safari we need to manually cancel the
117484                     // default gesture events.
117485                     container.on('gesturestart.ui gesturechange.ui gestureend.ui', function() {
117486                         // disable pinch-to-zoom of the UI via multitouch trackpads on macOS Safari
117487                         event.preventDefault();
117488                     });
117489                 }
117490
117491                 if ('PointerEvent' in window) {
117492                     select(window)
117493                         .on('pointerdown.ui pointerup.ui', function() {
117494                             var pointerType = event.pointerType || 'mouse';
117495                             if (_lastPointerType !== pointerType) {
117496                                 _lastPointerType = pointerType;
117497                                 container
117498                                     .attr('pointer', pointerType);
117499                             }
117500                         }, true);
117501                 } else {
117502                     _lastPointerType = 'mouse';
117503                     container
117504                         .attr('pointer', 'mouse');
117505                 }
117506
117507                 container
117508                     .attr('dir', _mainLocalizer.textDirection());
117509
117510                 // setup fullscreen keybindings (no button shown at this time)
117511                 container
117512                     .call(uiFullScreen(context));
117513
117514                 var map = context.map();
117515                 map.redrawEnable(false);  // don't draw until we've set zoom/lat/long
117516
117517                 map
117518                     .on('hitMinZoom.ui', function() {
117519                         ui.flash.text(_t('cannot_zoom'))();
117520                     });
117521
117522                 container
117523                     .append('svg')
117524                     .attr('id', 'ideditor-defs')
117525                     .call(svgDefs(context));
117526
117527                 container
117528                     .append('div')
117529                     .attr('class', 'sidebar')
117530                     .call(ui.sidebar);
117531
117532                 var content = container
117533                     .append('div')
117534                     .attr('class', 'main-content active');
117535
117536                 // Top toolbar
117537                 content
117538                     .append('div')
117539                     .attr('class', 'top-toolbar-wrap')
117540                     .append('div')
117541                     .attr('class', 'top-toolbar fillD')
117542                     .call(uiTopToolbar(context));
117543
117544                 content
117545                     .append('div')
117546                     .attr('class', 'main-map')
117547                     .attr('dir', 'ltr')
117548                     .call(map);
117549
117550                 content
117551                     .append('div')
117552                     .attr('class', 'spinner')
117553                     .call(uiSpinner(context));
117554
117555                 // Add attribution and footer
117556                 var about = content
117557                     .append('div')
117558                     .attr('class', 'map-footer');
117559
117560                 about
117561                     .append('div')
117562                     .attr('class', 'attribution-wrap')
117563                     .attr('dir', 'ltr')
117564                     .call(uiAttribution(context));
117565
117566                 about
117567                     .append('div')
117568                     .attr('class', 'api-status')
117569                     .call(uiStatus(context));
117570
117571
117572                 var footer = about
117573                     .append('div')
117574                     .attr('class', 'map-footer-bar fillD');
117575
117576                 footer
117577                     .append('div')
117578                     .attr('class', 'flash-wrap footer-hide');
117579
117580                 var footerWrap = footer
117581                     .append('div')
117582                     .attr('class', 'main-footer-wrap footer-show');
117583
117584                 footerWrap
117585                     .append('div')
117586                     .attr('class', 'scale-block')
117587                     .call(uiScale(context));
117588
117589                 var aboutList = footerWrap
117590                     .append('div')
117591                     .attr('class', 'info-block')
117592                     .append('ul')
117593                     .attr('class', 'map-footer-list');
117594
117595                 if (!context.embed()) {
117596                     aboutList
117597                         .call(uiAccount(context));
117598                 }
117599
117600                 aboutList
117601                     .append('li')
117602                     .attr('class', 'version')
117603                     .call(uiVersion(context));
117604
117605                 var issueLinks = aboutList
117606                     .append('li');
117607
117608                 issueLinks
117609                     .append('a')
117610                     .attr('target', '_blank')
117611                     .attr('href', 'https://github.com/openstreetmap/iD/issues')
117612                     .call(svgIcon('#iD-icon-bug', 'light'))
117613                     .call(uiTooltip().title(_t('report_a_bug')).placement('top'));
117614
117615                 issueLinks
117616                     .append('a')
117617                     .attr('target', '_blank')
117618                     .attr('href', 'https://github.com/openstreetmap/iD/blob/develop/CONTRIBUTING.md#translating')
117619                     .call(svgIcon('#iD-icon-translate', 'light'))
117620                     .call(uiTooltip().title(_t('help_translate')).placement('top'));
117621
117622                 aboutList
117623                     .append('li')
117624                     .attr('class', 'feature-warning')
117625                     .attr('tabindex', -1)
117626                     .call(uiFeatureInfo(context));
117627
117628                 aboutList
117629                     .append('li')
117630                     .attr('class', 'issues-info')
117631                     .attr('tabindex', -1)
117632                     .call(uiIssuesInfo(context));
117633
117634                 var apiConnections = context.apiConnections();
117635                 if (apiConnections && apiConnections.length > 1) {
117636                     aboutList
117637                         .append('li')
117638                         .attr('class', 'source-switch')
117639                         .attr('tabindex', -1)
117640                         .call(uiSourceSwitch(context)
117641                             .keys(apiConnections)
117642                         );
117643                 }
117644
117645                 aboutList
117646                     .append('li')
117647                     .attr('class', 'user-list')
117648                     .attr('tabindex', -1)
117649                     .call(uiContributors(context));
117650
117651
117652                 // Setup map dimensions and move map to initial center/zoom.
117653                 // This should happen after .main-content and toolbars exist.
117654                 ui.onResize();
117655                 map.redrawEnable(true);
117656
117657                 ui.hash = behaviorHash(context);
117658                 ui.hash();
117659                 if (!ui.hash.hadHash) {
117660                     map.centerZoom([0, 0], 2);
117661                 }
117662
117663
117664                 var overMap = content
117665                     .append('div')
117666                     .attr('class', 'over-map');
117667
117668                 // Map controls
117669                 var controls = overMap
117670                     .append('div')
117671                     .attr('class', 'map-controls');
117672
117673                 controls
117674                     .append('div')
117675                     .attr('class', 'map-control zoombuttons')
117676                     .call(uiZoom(context));
117677
117678                 controls
117679                     .append('div')
117680                     .attr('class', 'map-control zoom-to-selection-control')
117681                     .call(uiZoomToSelection(context));
117682
117683                 controls
117684                     .append('div')
117685                     .attr('class', 'map-control geolocate-control')
117686                     .call(uiGeolocate(context));
117687
117688                 // Add panes
117689                 // This should happen after map is initialized, as some require surface()
117690                 var panes = overMap
117691                     .append('div')
117692                     .attr('class', 'map-panes');
117693
117694                 var uiPanes = [
117695                     uiPaneBackground(context),
117696                     uiPaneMapData(context),
117697                     uiPaneIssues(context),
117698                     uiPanePreferences(context),
117699                     uiPaneHelp(context)
117700                 ];
117701
117702                 uiPanes.forEach(function(pane) {
117703                     controls
117704                         .append('div')
117705                         .attr('class', 'map-control map-pane-control ' + pane.id + '-control')
117706                         .call(pane.renderToggleButton);
117707
117708                     panes
117709                         .call(pane.renderPane);
117710                 });
117711
117712                 ui.info = uiInfo(context);
117713
117714                 // Add absolutely-positioned elements that sit on top of the map
117715                 // This should happen after the map is ready (center/zoom)
117716                 overMap
117717                     .call(uiMapInMap(context))
117718                     .call(ui.info)
117719                     .call(uiNotice(context));
117720
117721
117722                 overMap
117723                     .append('div')
117724                     .attr('class', 'photoviewer')
117725                     .classed('al', true)       // 'al'=left,  'ar'=right
117726                     .classed('hide', true)
117727                     .call(ui.photoviewer);
117728
117729
117730                 // Bind events
117731                 window.onbeforeunload = function() {
117732                     return context.save();
117733                 };
117734                 window.onunload = function() {
117735                     context.history().unlock();
117736                 };
117737
117738                 select(window)
117739                     .on('resize.editor', ui.onResize);
117740
117741
117742                 var panPixels = 80;
117743                 context.keybinding()
117744                     .on('⌫', function() { event.preventDefault(); })
117745                     .on([_t('sidebar.key'), '`', '²', '@'], ui.sidebar.toggle)   // #5663, #6864 - common QWERTY, AZERTY
117746                     .on('←', pan([panPixels, 0]))
117747                     .on('↑', pan([0, panPixels]))
117748                     .on('→', pan([-panPixels, 0]))
117749                     .on('↓', pan([0, -panPixels]))
117750                     .on(uiCmd('⌘←'), pan([map.dimensions()[0], 0]))
117751                     .on(uiCmd('⌘↑'), pan([0, map.dimensions()[1]]))
117752                     .on(uiCmd('⌘→'), pan([-map.dimensions()[0], 0]))
117753                     .on(uiCmd('⌘↓'), pan([0, -map.dimensions()[1]]))
117754                     .on(uiCmd('⌘' + _t('background.key')), function quickSwitch() {
117755                         if (event) {
117756                             event.stopImmediatePropagation();
117757                             event.preventDefault();
117758                         }
117759                         var previousBackground = context.background().findSource(corePreferences('background-last-used-toggle'));
117760                         if (previousBackground) {
117761                             var currentBackground = context.background().baseLayerSource();
117762                             corePreferences('background-last-used-toggle', currentBackground.id);
117763                             corePreferences('background-last-used', previousBackground.id);
117764                             context.background().baseLayerSource(previousBackground);
117765                         }
117766                     })
117767                     .on(_t('area_fill.wireframe.key'), function toggleWireframe() {
117768                         event.preventDefault();
117769                         event.stopPropagation();
117770                         context.map().toggleWireframe();
117771                     })
117772                     .on(uiCmd('⌥' + _t('area_fill.wireframe.key')), function toggleOsmData() {
117773                         event.preventDefault();
117774                         event.stopPropagation();
117775
117776                         // Don't allow layer changes while drawing - #6584
117777                         var mode = context.mode();
117778                         if (mode && /^draw/.test(mode.id)) { return; }
117779
117780                         var layer = context.layers().layer('osm');
117781                         if (layer) {
117782                             layer.enabled(!layer.enabled());
117783                             if (!layer.enabled()) {
117784                                 context.enter(modeBrowse(context));
117785                             }
117786                         }
117787                     })
117788                     .on(_t('map_data.highlight_edits.key'), function toggleHighlightEdited() {
117789                         event.preventDefault();
117790                         context.map().toggleHighlightEdited();
117791                     });
117792
117793                 context
117794                     .on('enter.editor', function(entered) {
117795                         container
117796                             .classed('mode-' + entered.id, true);
117797                     })
117798                     .on('exit.editor', function(exited) {
117799                         container
117800                             .classed('mode-' + exited.id, false);
117801                     });
117802
117803                 context.enter(modeBrowse(context));
117804
117805                 if (!_initCounter++) {
117806                     if (!ui.hash.startWalkthrough) {
117807                         context.container()
117808                             .call(uiSplash(context))
117809                             .call(uiRestore(context));
117810                     }
117811
117812                     context.container()
117813                         .call(uiShortcuts(context));
117814                 }
117815
117816                 var osm = context.connection();
117817                 var auth = uiLoading(context).message(_t('loading_auth')).blocking(true);
117818
117819                 if (osm && auth) {
117820                     osm
117821                         .on('authLoading.ui', function() {
117822                             context.container()
117823                                 .call(auth);
117824                         })
117825                         .on('authDone.ui', function() {
117826                             auth.close();
117827                         });
117828                 }
117829
117830                 _initCounter++;
117831
117832                 if (ui.hash.startWalkthrough) {
117833                     ui.hash.startWalkthrough = false;
117834                     context.container().call(uiIntro(context));
117835                 }
117836
117837
117838                 function pan(d) {
117839                     return function() {
117840                         if (event.shiftKey) { return; }
117841                         if (context.container().select('.combobox').size()) { return; }
117842                         event.preventDefault();
117843                         context.map().pan(d, 100);
117844                     };
117845                 }
117846
117847             }
117848
117849
117850             var ui = {};
117851
117852             var _loadPromise;
117853             // renders the iD interface into the container node
117854             ui.ensureLoaded = function () {
117855
117856                 if (_loadPromise) { return _loadPromise; }
117857
117858                 return _loadPromise = Promise.all([
117859                         // must have strings and presets before loading the UI
117860                         _mainLocalizer.ensureLoaded(),
117861                         _mainPresetIndex.ensureLoaded()
117862                     ])
117863                     .then(function () {
117864                         if (!context.container().empty()) { render(context.container()); }
117865                     })
117866                     .catch(function (err) { return console.error(err); });  // eslint-disable-line
117867             };
117868
117869
117870             // `ui.restart()` will destroy and rebuild the entire iD interface,
117871             // for example to switch the locale while iD is running.
117872             ui.restart = function() {
117873                 context.keybinding().clear();
117874
117875                 _loadPromise = null;
117876
117877                 context.container().selectAll('*').remove();
117878
117879                 ui.ensureLoaded();
117880             };
117881
117882             ui.lastPointerType = function() {
117883                 return _lastPointerType;
117884             };
117885
117886             ui.flash = uiFlash(context);
117887
117888             ui.sidebar = uiSidebar(context);
117889
117890             ui.photoviewer = uiPhotoviewer(context);
117891
117892             ui.onResize = function(withPan) {
117893                 var map = context.map();
117894
117895                 // Recalc dimensions of map and sidebar.. (`true` = force recalc)
117896                 // This will call `getBoundingClientRect` and trigger reflow,
117897                 //  but the values will be cached for later use.
117898                 var mapDimensions = utilGetDimensions(context.container().select('.main-content'), true);
117899                 utilGetDimensions(context.container().select('.sidebar'), true);
117900
117901                 if (withPan !== undefined) {
117902                     map.redrawEnable(false);
117903                     map.pan(withPan);
117904                     map.redrawEnable(true);
117905                 }
117906                 map.dimensions(mapDimensions);
117907
117908                 ui.photoviewer.onMapResize();
117909
117910                 // check if header or footer have overflowed
117911                 ui.checkOverflow('.top-toolbar');
117912                 ui.checkOverflow('.map-footer-bar');
117913
117914                 // Use outdated code so it works on Explorer
117915                 var resizeWindowEvent = document.createEvent('Event');
117916
117917                 resizeWindowEvent.initEvent('resizeWindow', true, true);
117918
117919                 document.dispatchEvent(resizeWindowEvent);
117920             };
117921
117922
117923             // Call checkOverflow when resizing or whenever the contents change.
117924             ui.checkOverflow = function(selector, reset) {
117925                 if (reset) {
117926                     delete _needWidth[selector];
117927                 }
117928
117929                 var element = select(selector);
117930                 var scrollWidth = element.property('scrollWidth');
117931                 var clientWidth = element.property('clientWidth');
117932                 var needed = _needWidth[selector] || scrollWidth;
117933
117934                 if (scrollWidth > clientWidth) {    // overflow happening
117935                     element.classed('narrow', true);
117936                     if (!_needWidth[selector]) {
117937                         _needWidth[selector] = scrollWidth;
117938                     }
117939
117940                 } else if (scrollWidth >= needed) {
117941                     element.classed('narrow', false);
117942                 }
117943             };
117944
117945             ui.togglePanes = function(showPane) {
117946                 var shownPanes = context.container().selectAll('.map-pane.shown');
117947
117948                 var side = _mainLocalizer.textDirection() === 'ltr' ? 'right' : 'left';
117949
117950                 shownPanes
117951                     .classed('shown', false);
117952
117953                 context.container().selectAll('.map-pane-control button')
117954                     .classed('active', false);
117955
117956                 if (showPane) {
117957                     shownPanes
117958                         .style('display', 'none')
117959                         .style(side, '-500px');
117960
117961                     context.container().selectAll('.' + showPane.attr('pane') + '-control button')
117962                         .classed('active', true);
117963
117964                     showPane
117965                         .classed('shown', true)
117966                         .style('display', 'block');
117967                     if (shownPanes.empty()) {
117968                         showPane
117969                             .style('display', 'block')
117970                             .style(side, '-500px')
117971                             .transition()
117972                             .duration(200)
117973                             .style(side, '0px');
117974                     } else {
117975                         showPane
117976                             .style(side, '0px');
117977                     }
117978                 } else {
117979                     shownPanes
117980                         .style('display', 'block')
117981                         .style(side, '0px')
117982                         .transition()
117983                         .duration(200)
117984                         .style(side, '-500px')
117985                         .on('end', function() {
117986                             select(this).style('display', 'none');
117987                         });
117988                 }
117989             };
117990
117991
117992             var _editMenu = uiEditMenu(context);
117993
117994             ui.editMenu = function() {
117995                 return _editMenu;
117996             };
117997
117998             ui.showEditMenu = function(anchorPoint, triggerType, operations) {
117999
118000                 // remove any displayed menu
118001                 ui.closeEditMenu();
118002
118003                 if (!operations && context.mode().operations) { operations = context.mode().operations(); }
118004                 if (!operations || !operations.length) { return; }
118005
118006                 // disable menu if in wide selection, for example
118007                 if (!context.map().editableDataEnabled()) { return; }
118008
118009                 var surfaceNode = context.surface().node();
118010                 if (surfaceNode.focus) {   // FF doesn't support it
118011                     // focus the surface or else clicking off the menu may not trigger modeBrowse
118012                     surfaceNode.focus();
118013                 }
118014
118015                 operations.forEach(function(operation) {
118016                     if (operation.point) { operation.point(anchorPoint); }
118017                 });
118018
118019                 _editMenu
118020                     .anchorLoc(anchorPoint)
118021                     .triggerType(triggerType)
118022                     .operations(operations);
118023
118024                 // render the menu
118025                 context.map().supersurface.call(_editMenu);
118026             };
118027
118028             ui.closeEditMenu = function() {
118029                 // remove any existing menu no matter how it was added
118030                 context.map().supersurface
118031                     .select('.edit-menu').remove();
118032             };
118033
118034
118035             var _saveLoading = select(null);
118036
118037             context.uploader()
118038                 .on('saveStarted.ui', function() {
118039                     _saveLoading = uiLoading(context)
118040                         .message(_t('save.uploading'))
118041                         .blocking(true);
118042                     context.container().call(_saveLoading);  // block input during upload
118043                 })
118044                 .on('saveEnded.ui', function() {
118045                     _saveLoading.close();
118046                     _saveLoading = select(null);
118047                 });
118048
118049             return ui;
118050         }
118051
118052         function coreContext() {
118053           var this$1 = this;
118054
118055           var dispatch$1 = dispatch('enter', 'exit', 'change');
118056           var context = utilRebind({}, dispatch$1, 'on');
118057           var _deferred = new Set();
118058
118059           context.version = '2.18.5';
118060           context.privacyVersion = '20200407';
118061
118062           // iD will alter the hash so cache the parameters intended to setup the session
118063           context.initialHashParams = window.location.hash ? utilStringQs(window.location.hash) : {};
118064
118065           context.isFirstSession = !corePreferences('sawSplash') && !corePreferences('sawPrivacyVersion');
118066
118067
118068           /* Changeset */
118069           // An osmChangeset object. Not loaded until needed.
118070           context.changeset = null;
118071
118072           var _defaultChangesetComment = context.initialHashParams.comment;
118073           var _defaultChangesetSource = context.initialHashParams.source;
118074           var _defaultChangesetHashtags = context.initialHashParams.hashtags;
118075           context.defaultChangesetComment = function(val) {
118076             if (!arguments.length) { return _defaultChangesetComment; }
118077             _defaultChangesetComment = val;
118078             return context;
118079           };
118080           context.defaultChangesetSource = function(val) {
118081             if (!arguments.length) { return _defaultChangesetSource; }
118082             _defaultChangesetSource = val;
118083             return context;
118084           };
118085           context.defaultChangesetHashtags = function(val) {
118086             if (!arguments.length) { return _defaultChangesetHashtags; }
118087             _defaultChangesetHashtags = val;
118088             return context;
118089           };
118090
118091           /* Document title */
118092           /* (typically shown as the label for the browser window/tab) */
118093
118094           // If true, iD will update the title based on what the user is doing
118095           var _setsDocumentTitle = true;
118096           context.setsDocumentTitle = function(val) {
118097             if (!arguments.length) { return _setsDocumentTitle; }
118098             _setsDocumentTitle = val;
118099             return context;
118100           };
118101           // The part of the title that is always the same
118102           var _documentTitleBase = document.title;
118103           context.documentTitleBase = function(val) {
118104             if (!arguments.length) { return _documentTitleBase; }
118105             _documentTitleBase = val;
118106             return context;
118107           };
118108
118109
118110           /* User interface and keybinding */
118111           var _ui;
118112           context.ui = function () { return _ui; };
118113           context.lastPointerType = function () { return _ui.lastPointerType(); };
118114
118115           var _keybinding = utilKeybinding('context');
118116           context.keybinding = function () { return _keybinding; };
118117           select(document).call(_keybinding);
118118
118119
118120           /* Straight accessors. Avoid using these if you can. */
118121           // Instantiate the connection here because it doesn't require passing in
118122           // `context` and it's needed for pre-init calls like `preauth`
118123           var _connection = services.osm;
118124           var _history;
118125           var _validator;
118126           var _uploader;
118127           context.connection = function () { return _connection; };
118128           context.history = function () { return _history; };
118129           context.validator = function () { return _validator; };
118130           context.uploader = function () { return _uploader; };
118131
118132           /* Connection */
118133           context.preauth = function (options) {
118134             if (_connection) {
118135               _connection.switch(options);
118136             }
118137             return context;
118138           };
118139
118140           /* connection options for source switcher (optional) */
118141           var _apiConnections;
118142           context.apiConnections = function(val) {
118143             if (!arguments.length) { return _apiConnections; }
118144             _apiConnections = val;
118145             return context;
118146           };
118147
118148
118149           // A string or array or locale codes to prefer over the browser's settings
118150           context.locale = function(locale) {
118151             if (!arguments.length) { return _mainLocalizer.localeCode(); }
118152             _mainLocalizer.preferredLocaleCodes(locale);
118153             return context;
118154           };
118155
118156
118157           function afterLoad(cid, callback) {
118158             return function (err, result) {
118159               if (err) {
118160                 // 400 Bad Request, 401 Unauthorized, 403 Forbidden..
118161                 if (err.status === 400 || err.status === 401 || err.status === 403) {
118162                   if (_connection) {
118163                     _connection.logout();
118164                   }
118165                 }
118166                 if (typeof callback === 'function') {
118167                   callback(err);
118168                 }
118169                 return;
118170
118171               } else if (_connection && _connection.getConnectionId() !== cid) {
118172                 if (typeof callback === 'function') {
118173                   callback({ message: 'Connection Switched', status: -1 });
118174                 }
118175                 return;
118176
118177               } else {
118178                 _history.merge(result.data, result.extent);
118179                 if (typeof callback === 'function') {
118180                   callback(err, result);
118181                 }
118182                 return;
118183               }
118184             };
118185           }
118186
118187
118188           context.loadTiles = function (projection, callback) {
118189             var handle = window.requestIdleCallback(function () {
118190               _deferred.delete(handle);
118191               if (_connection && context.editableDataEnabled()) {
118192                 var cid = _connection.getConnectionId();
118193                 _connection.loadTiles(projection, afterLoad(cid, callback));
118194               }
118195             });
118196             _deferred.add(handle);
118197           };
118198
118199           context.loadTileAtLoc = function (loc, callback) {
118200             var handle = window.requestIdleCallback(function () {
118201               _deferred.delete(handle);
118202               if (_connection && context.editableDataEnabled()) {
118203                 var cid = _connection.getConnectionId();
118204                 _connection.loadTileAtLoc(loc, afterLoad(cid, callback));
118205               }
118206             });
118207             _deferred.add(handle);
118208           };
118209
118210           context.loadEntity = function (entityID, callback) {
118211             if (_connection) {
118212               var cid = _connection.getConnectionId();
118213               _connection.loadEntity(entityID, afterLoad(cid, callback));
118214             }
118215           };
118216
118217           context.zoomToEntity = function (entityID, zoomTo) {
118218             if (zoomTo !== false) {
118219               context.loadEntity(entityID, function (err, result) {
118220                 if (err) { return; }
118221                 var entity = result.data.find(function (e) { return e.id === entityID; });
118222                 if (entity) {
118223                   _map.zoomTo(entity);
118224                 }
118225               });
118226             }
118227
118228             _map.on('drawn.zoomToEntity', function () {
118229               if (!context.hasEntity(entityID)) { return; }
118230               _map.on('drawn.zoomToEntity', null);
118231               context.on('enter.zoomToEntity', null);
118232               context.enter(modeSelect(context, [entityID]));
118233             });
118234
118235             context.on('enter.zoomToEntity', function () {
118236               if (_mode.id !== 'browse') {
118237                 _map.on('drawn.zoomToEntity', null);
118238                 context.on('enter.zoomToEntity', null);
118239               }
118240             });
118241           };
118242
118243           var _minEditableZoom = 16;
118244           context.minEditableZoom = function(val) {
118245             if (!arguments.length) { return _minEditableZoom; }
118246             _minEditableZoom = val;
118247             if (_connection) {
118248               _connection.tileZoom(val);
118249             }
118250             return context;
118251           };
118252
118253           // String length limits in Unicode characters, not JavaScript UTF-16 code units
118254           context.maxCharsForTagKey = function () { return 255; };
118255           context.maxCharsForTagValue = function () { return 255; };
118256           context.maxCharsForRelationRole = function () { return 255; };
118257
118258           function cleanOsmString(val, maxChars) {
118259             // be lenient with input
118260             if (val === undefined || val === null) {
118261               val = '';
118262             } else {
118263               val = val.toString();
118264             }
118265
118266             // remove whitespace
118267             val = val.trim();
118268
118269             // use the canonical form of the string
118270             if (val.normalize) { val = val.normalize('NFC'); }
118271
118272             // trim to the number of allowed characters
118273             return utilUnicodeCharsTruncated(val, maxChars);
118274           }
118275           context.cleanTagKey = function (val) { return cleanOsmString(val, context.maxCharsForTagKey()); };
118276           context.cleanTagValue = function (val) { return cleanOsmString(val, context.maxCharsForTagValue()); };
118277           context.cleanRelationRole = function (val) { return cleanOsmString(val, context.maxCharsForRelationRole()); };
118278
118279
118280           /* History */
118281           var _inIntro = false;
118282           context.inIntro = function(val) {
118283             if (!arguments.length) { return _inIntro; }
118284             _inIntro = val;
118285             return context;
118286           };
118287
118288           // Immediately save the user's history to localstorage, if possible
118289           // This is called someteimes, but also on the `window.onbeforeunload` handler
118290           context.save = function () {
118291             // no history save, no message onbeforeunload
118292             if (_inIntro || context.container().select('.modal').size()) { return; }
118293
118294             var canSave;
118295             if (_mode && _mode.id === 'save') {
118296               canSave = false;
118297
118298               // Attempt to prevent user from creating duplicate changes - see #5200
118299               if (services.osm && services.osm.isChangesetInflight()) {
118300                 _history.clearSaved();
118301                 return;
118302               }
118303
118304             } else {
118305               canSave = context.selectedIDs().every(function (id) {
118306                 var entity = context.hasEntity(id);
118307                 return entity && !entity.isDegenerate();
118308               });
118309             }
118310
118311             if (canSave) {
118312               _history.save();
118313             }
118314             if (_history.hasChanges()) {
118315               return _t('save.unsaved_changes');
118316             }
118317           };
118318
118319           // Debounce save, since it's a synchronous localStorage write,
118320           // and history changes can happen frequently (e.g. when dragging).
118321           context.debouncedSave = debounce(context.save, 350);
118322
118323           function withDebouncedSave(fn) {
118324             return function() {
118325               var result = fn.apply(_history, arguments);
118326               context.debouncedSave();
118327               return result;
118328             };
118329           }
118330
118331
118332           /* Graph */
118333           context.hasEntity = function (id) { return _history.graph().hasEntity(id); };
118334           context.entity = function (id) { return _history.graph().entity(id); };
118335
118336
118337           /* Modes */
118338           var _mode;
118339           context.mode = function () { return _mode; };
118340           context.enter = function (newMode) {
118341             if (_mode) {
118342               _mode.exit();
118343               dispatch$1.call('exit', this$1, _mode);
118344             }
118345
118346             _mode = newMode;
118347             _mode.enter();
118348             dispatch$1.call('enter', this$1, _mode);
118349           };
118350
118351           context.selectedIDs = function () { return (_mode && _mode.selectedIDs && _mode.selectedIDs()) || []; };
118352           context.activeID = function () { return _mode && _mode.activeID && _mode.activeID(); };
118353
118354           var _selectedNoteID;
118355           context.selectedNoteID = function(noteID) {
118356             if (!arguments.length) { return _selectedNoteID; }
118357             _selectedNoteID = noteID;
118358             return context;
118359           };
118360
118361           // NOTE: Don't change the name of this until UI v3 is merged
118362           var _selectedErrorID;
118363           context.selectedErrorID = function(errorID) {
118364             if (!arguments.length) { return _selectedErrorID; }
118365             _selectedErrorID = errorID;
118366             return context;
118367           };
118368
118369
118370           /* Behaviors */
118371           context.install = function (behavior) { return context.surface().call(behavior); };
118372           context.uninstall = function (behavior) { return context.surface().call(behavior.off); };
118373
118374
118375           /* Copy/Paste */
118376           var _copyGraph;
118377           context.copyGraph = function () { return _copyGraph; };
118378
118379           var _copyIDs = [];
118380           context.copyIDs = function(val) {
118381             if (!arguments.length) { return _copyIDs; }
118382             _copyIDs = val;
118383             _copyGraph = _history.graph();
118384             return context;
118385           };
118386
118387           var _copyLonLat;
118388           context.copyLonLat = function(val) {
118389             if (!arguments.length) { return _copyLonLat; }
118390             _copyLonLat = val;
118391             return context;
118392           };
118393
118394
118395           /* Background */
118396           var _background;
118397           context.background = function () { return _background; };
118398
118399
118400           /* Features */
118401           var _features;
118402           context.features = function () { return _features; };
118403           context.hasHiddenConnections = function (id) {
118404             var graph = _history.graph();
118405             var entity = graph.entity(id);
118406             return _features.hasHiddenConnections(entity, graph);
118407           };
118408
118409
118410           /* Photos */
118411           var _photos;
118412           context.photos = function () { return _photos; };
118413
118414
118415           /* Map */
118416           var _map;
118417           context.map = function () { return _map; };
118418           context.layers = function () { return _map.layers(); };
118419           context.surface = function () { return _map.surface; };
118420           context.editableDataEnabled = function () { return _map.editableDataEnabled(); };
118421           context.surfaceRect = function () { return _map.surface.node().getBoundingClientRect(); };
118422           context.editable = function () {
118423             // don't allow editing during save
118424             var mode = context.mode();
118425             if (!mode || mode.id === 'save') { return false; }
118426             return _map.editableDataEnabled();
118427           };
118428
118429
118430           /* Debug */
118431           var _debugFlags = {
118432             tile: false,        // tile boundaries
118433             collision: false,   // label collision bounding boxes
118434             imagery: false,     // imagery bounding polygons
118435             target: false,      // touch targets
118436             downloaded: false   // downloaded data from osm
118437           };
118438           context.debugFlags = function () { return _debugFlags; };
118439           context.getDebug = function (flag) { return flag && _debugFlags[flag]; };
118440           context.setDebug = function(flag, val) {
118441             if (arguments.length === 1) { val = true; }
118442             _debugFlags[flag] = val;
118443             dispatch$1.call('change');
118444             return context;
118445           };
118446
118447
118448           /* Container */
118449           var _container = select(null);
118450           context.container = function(val) {
118451             if (!arguments.length) { return _container; }
118452             _container = val;
118453             _container.classed('ideditor', true);
118454             return context;
118455           };
118456           context.containerNode = function(val) {
118457             if (!arguments.length) { return context.container().node(); }
118458             context.container(select(val));
118459             return context;
118460           };
118461
118462           var _embed;
118463           context.embed = function(val) {
118464             if (!arguments.length) { return _embed; }
118465             _embed = val;
118466             return context;
118467           };
118468
118469
118470           /* Assets */
118471           var _assetPath = '';
118472           context.assetPath = function(val) {
118473             if (!arguments.length) { return _assetPath; }
118474             _assetPath = val;
118475             _mainFileFetcher.assetPath(val);
118476             return context;
118477           };
118478
118479           var _assetMap = {};
118480           context.assetMap = function(val) {
118481             if (!arguments.length) { return _assetMap; }
118482             _assetMap = val;
118483             _mainFileFetcher.assetMap(val);
118484             return context;
118485           };
118486
118487           context.asset = function (val) {
118488             if (/^http(s)?:\/\//i.test(val)) { return val; }
118489             var filename = _assetPath + val;
118490             return _assetMap[filename] || filename;
118491           };
118492
118493           context.imagePath = function (val) { return context.asset(("img/" + val)); };
118494
118495
118496           /* reset (aka flush) */
118497           context.reset = context.flush = function () {
118498             context.debouncedSave.cancel();
118499
118500             Array.from(_deferred).forEach(function (handle) {
118501               window.cancelIdleCallback(handle);
118502               _deferred.delete(handle);
118503             });
118504
118505             Object.values(services).forEach(function (service) {
118506               if (service && typeof service.reset === 'function') {
118507                 service.reset(context);
118508               }
118509             });
118510
118511             context.changeset = null;
118512
118513             _validator.reset();
118514             _features.reset();
118515             _history.reset();
118516             _uploader.reset();
118517
118518             // don't leave stale state in the inspector
118519             context.container().select('.inspector-wrap *').remove();
118520
118521             return context;
118522           };
118523
118524
118525           /* Projections */
118526           context.projection = geoRawMercator();
118527           context.curtainProjection = geoRawMercator();
118528
118529
118530           /* Init */
118531           context.init = function () {
118532
118533             instantiateInternal();
118534
118535             initializeDependents();
118536
118537             return context;
118538
118539             // Load variables and properties. No property of `context` should be accessed
118540             // until this is complete since load statuses are indeterminate. The order
118541             // of instantiation shouldn't matter.
118542             function instantiateInternal() {
118543
118544               _history = coreHistory(context);
118545               context.graph = _history.graph;
118546               context.pauseChangeDispatch = _history.pauseChangeDispatch;
118547               context.resumeChangeDispatch = _history.resumeChangeDispatch;
118548               context.perform = withDebouncedSave(_history.perform);
118549               context.replace = withDebouncedSave(_history.replace);
118550               context.pop = withDebouncedSave(_history.pop);
118551               context.overwrite = withDebouncedSave(_history.overwrite);
118552               context.undo = withDebouncedSave(_history.undo);
118553               context.redo = withDebouncedSave(_history.redo);
118554
118555               _validator = coreValidator(context);
118556               _uploader = coreUploader(context);
118557
118558               _background = rendererBackground(context);
118559               _features = rendererFeatures(context);
118560               _map = rendererMap(context);
118561               _photos = rendererPhotos(context);
118562
118563               _ui = uiInit(context);
118564             }
118565
118566             // Set up objects that might need to access properties of `context`. The order
118567             // might matter if dependents make calls to each other. Be wary of async calls.
118568             function initializeDependents() {
118569
118570               if (context.initialHashParams.presets) {
118571                 _mainPresetIndex.addablePresetIDs(new Set(context.initialHashParams.presets.split(',')));
118572               }
118573
118574               if (context.initialHashParams.locale) {
118575                 _mainLocalizer.preferredLocaleCodes(context.initialHashParams.locale);
118576               }
118577
118578               // kick off some async work
118579               _mainLocalizer.ensureLoaded();
118580               _background.ensureLoaded();
118581               _mainPresetIndex.ensureLoaded();
118582
118583               Object.values(services).forEach(function (service) {
118584                 if (service && typeof service.init === 'function') {
118585                   service.init();
118586                 }
118587               });
118588
118589               _map.init();
118590               _validator.init();
118591               _features.init();
118592               _photos.init();
118593
118594               if (services.maprules && context.initialHashParams.maprules) {
118595                 d3_json(context.initialHashParams.maprules)
118596                   .then(function (mapcss) {
118597                     services.maprules.init();
118598                     mapcss.forEach(function (mapcssSelector) { return services.maprules.addRule(mapcssSelector); });
118599                   })
118600                   .catch(function () { /* ignore */ });
118601               }
118602
118603               // if the container isn't available, e.g. when testing, don't load the UI
118604               if (!context.container().empty()) { _ui.ensureLoaded(); }
118605             }
118606           };
118607
118608
118609           return context;
118610         }
118611
118612         // When `debug = true`, we use `Object.freeze` on immutables in iD.
118613         // This is only done in testing because of the performance penalty.
118614         var debug = false;
118615         var d3 = {
118616           customEvent: customEvent,
118617           dispatch:  dispatch,
118618           event:  event,
118619           geoMercator: mercator,
118620           geoProjection: projection,
118621           polygonArea: d3_polygonArea,
118622           polygonCentroid: d3_polygonCentroid,
118623           select: select,
118624           selectAll: selectAll,
118625           timerFlush: timerFlush
118626         };
118627
118628         var iD = /*#__PURE__*/Object.freeze({
118629                 __proto__: null,
118630                 debug: debug,
118631                 d3: d3,
118632                 actionAddEntity: actionAddEntity,
118633                 actionAddMember: actionAddMember,
118634                 actionAddMidpoint: actionAddMidpoint,
118635                 actionAddVertex: actionAddVertex,
118636                 actionChangeMember: actionChangeMember,
118637                 actionChangePreset: actionChangePreset,
118638                 actionChangeTags: actionChangeTags,
118639                 actionCircularize: actionCircularize,
118640                 actionConnect: actionConnect,
118641                 actionCopyEntities: actionCopyEntities,
118642                 actionDeleteMember: actionDeleteMember,
118643                 actionDeleteMultiple: actionDeleteMultiple,
118644                 actionDeleteNode: actionDeleteNode,
118645                 actionDeleteRelation: actionDeleteRelation,
118646                 actionDeleteWay: actionDeleteWay,
118647                 actionDiscardTags: actionDiscardTags,
118648                 actionDisconnect: actionDisconnect,
118649                 actionExtract: actionExtract,
118650                 actionJoin: actionJoin,
118651                 actionMerge: actionMerge,
118652                 actionMergeNodes: actionMergeNodes,
118653                 actionMergePolygon: actionMergePolygon,
118654                 actionMergeRemoteChanges: actionMergeRemoteChanges,
118655                 actionMove: actionMove,
118656                 actionMoveMember: actionMoveMember,
118657                 actionMoveNode: actionMoveNode,
118658                 actionNoop: actionNoop,
118659                 actionOrthogonalize: actionOrthogonalize,
118660                 actionRestrictTurn: actionRestrictTurn,
118661                 actionReverse: actionReverse,
118662                 actionRevert: actionRevert,
118663                 actionRotate: actionRotate,
118664                 actionSplit: actionSplit,
118665                 actionStraightenNodes: actionStraightenNodes,
118666                 actionStraightenWay: actionStraightenWay,
118667                 actionUnrestrictTurn: actionUnrestrictTurn,
118668                 actionReflect: actionReflect,
118669                 actionUpgradeTags: actionUpgradeTags,
118670                 behaviorAddWay: behaviorAddWay,
118671                 behaviorBreathe: behaviorBreathe,
118672                 behaviorDrag: behaviorDrag,
118673                 behaviorDrawWay: behaviorDrawWay,
118674                 behaviorDraw: behaviorDraw,
118675                 behaviorEdit: behaviorEdit,
118676                 behaviorHash: behaviorHash,
118677                 behaviorHover: behaviorHover,
118678                 behaviorLasso: behaviorLasso,
118679                 behaviorOperation: behaviorOperation,
118680                 behaviorPaste: behaviorPaste,
118681                 behaviorSelect: behaviorSelect,
118682                 coreContext: coreContext,
118683                 coreFileFetcher: coreFileFetcher,
118684                 fileFetcher: _mainFileFetcher,
118685                 coreDifference: coreDifference,
118686                 coreGraph: coreGraph,
118687                 coreHistory: coreHistory,
118688                 coreLocalizer: coreLocalizer,
118689                 t: _t,
118690                 localizer: _mainLocalizer,
118691                 prefs: corePreferences,
118692                 coreTree: coreTree,
118693                 coreUploader: coreUploader,
118694                 coreValidator: coreValidator,
118695                 geoExtent: geoExtent,
118696                 geoLatToMeters: geoLatToMeters,
118697                 geoLonToMeters: geoLonToMeters,
118698                 geoMetersToLat: geoMetersToLat,
118699                 geoMetersToLon: geoMetersToLon,
118700                 geoMetersToOffset: geoMetersToOffset,
118701                 geoOffsetToMeters: geoOffsetToMeters,
118702                 geoScaleToZoom: geoScaleToZoom,
118703                 geoSphericalClosestNode: geoSphericalClosestNode,
118704                 geoSphericalDistance: geoSphericalDistance,
118705                 geoZoomToScale: geoZoomToScale,
118706                 geoAngle: geoAngle,
118707                 geoChooseEdge: geoChooseEdge,
118708                 geoEdgeEqual: geoEdgeEqual,
118709                 geoGetSmallestSurroundingRectangle: geoGetSmallestSurroundingRectangle,
118710                 geoHasLineIntersections: geoHasLineIntersections,
118711                 geoHasSelfIntersections: geoHasSelfIntersections,
118712                 geoRotate: geoRotate,
118713                 geoLineIntersection: geoLineIntersection,
118714                 geoPathHasIntersections: geoPathHasIntersections,
118715                 geoPathIntersections: geoPathIntersections,
118716                 geoPathLength: geoPathLength,
118717                 geoPointInPolygon: geoPointInPolygon,
118718                 geoPolygonContainsPolygon: geoPolygonContainsPolygon,
118719                 geoPolygonIntersectsPolygon: geoPolygonIntersectsPolygon,
118720                 geoViewportEdge: geoViewportEdge,
118721                 geoRawMercator: geoRawMercator,
118722                 geoVecAdd: geoVecAdd,
118723                 geoVecAngle: geoVecAngle,
118724                 geoVecCross: geoVecCross,
118725                 geoVecDot: geoVecDot,
118726                 geoVecEqual: geoVecEqual,
118727                 geoVecFloor: geoVecFloor,
118728                 geoVecInterp: geoVecInterp,
118729                 geoVecLength: geoVecLength,
118730                 geoVecLengthSquare: geoVecLengthSquare,
118731                 geoVecNormalize: geoVecNormalize,
118732                 geoVecNormalizedDot: geoVecNormalizedDot,
118733                 geoVecProject: geoVecProject,
118734                 geoVecSubtract: geoVecSubtract,
118735                 geoVecScale: geoVecScale,
118736                 geoOrthoNormalizedDotProduct: geoOrthoNormalizedDotProduct,
118737                 geoOrthoCalcScore: geoOrthoCalcScore,
118738                 geoOrthoMaxOffsetAngle: geoOrthoMaxOffsetAngle,
118739                 geoOrthoCanOrthogonalize: geoOrthoCanOrthogonalize,
118740                 modeAddArea: modeAddArea,
118741                 modeAddLine: modeAddLine,
118742                 modeAddPoint: modeAddPoint,
118743                 modeAddNote: modeAddNote,
118744                 modeBrowse: modeBrowse,
118745                 modeDragNode: modeDragNode,
118746                 modeDragNote: modeDragNote,
118747                 modeDrawArea: modeDrawArea,
118748                 modeDrawLine: modeDrawLine,
118749                 modeMove: modeMove,
118750                 modeRotate: modeRotate,
118751                 modeSave: modeSave,
118752                 modeSelect: modeSelect,
118753                 modeSelectData: modeSelectData,
118754                 modeSelectError: modeSelectError,
118755                 modeSelectNote: modeSelectNote,
118756                 operationCircularize: operationCircularize,
118757                 operationContinue: operationContinue,
118758                 operationCopy: operationCopy,
118759                 operationDelete: operationDelete,
118760                 operationDisconnect: operationDisconnect,
118761                 operationDowngrade: operationDowngrade,
118762                 operationExtract: operationExtract,
118763                 operationMerge: operationMerge,
118764                 operationMove: operationMove,
118765                 operationOrthogonalize: operationOrthogonalize,
118766                 operationPaste: operationPaste,
118767                 operationReflectShort: operationReflectShort,
118768                 operationReflectLong: operationReflectLong,
118769                 operationReverse: operationReverse,
118770                 operationRotate: operationRotate,
118771                 operationSplit: operationSplit,
118772                 operationStraighten: operationStraighten,
118773                 osmChangeset: osmChangeset,
118774                 osmEntity: osmEntity,
118775                 osmNode: osmNode,
118776                 osmNote: osmNote,
118777                 osmRelation: osmRelation,
118778                 osmWay: osmWay,
118779                 QAItem: QAItem,
118780                 osmIntersection: osmIntersection,
118781                 osmTurn: osmTurn,
118782                 osmInferRestriction: osmInferRestriction,
118783                 osmLanes: osmLanes,
118784                 osmOldMultipolygonOuterMemberOfRelation: osmOldMultipolygonOuterMemberOfRelation,
118785                 osmIsOldMultipolygonOuterMember: osmIsOldMultipolygonOuterMember,
118786                 osmOldMultipolygonOuterMember: osmOldMultipolygonOuterMember,
118787                 osmJoinWays: osmJoinWays,
118788                 get osmAreaKeys () { return osmAreaKeys; },
118789                 osmSetAreaKeys: osmSetAreaKeys,
118790                 osmTagSuggestingArea: osmTagSuggestingArea,
118791                 get osmPointTags () { return osmPointTags; },
118792                 osmSetPointTags: osmSetPointTags,
118793                 get osmVertexTags () { return osmVertexTags; },
118794                 osmSetVertexTags: osmSetVertexTags,
118795                 osmNodeGeometriesForTags: osmNodeGeometriesForTags,
118796                 osmOneWayTags: osmOneWayTags,
118797                 osmPavedTags: osmPavedTags,
118798                 osmIsInterestingTag: osmIsInterestingTag,
118799                 osmRoutableHighwayTagValues: osmRoutableHighwayTagValues,
118800                 osmFlowingWaterwayTagValues: osmFlowingWaterwayTagValues,
118801                 osmRailwayTrackTagValues: osmRailwayTrackTagValues,
118802                 presetCategory: presetCategory,
118803                 presetCollection: presetCollection,
118804                 presetField: presetField,
118805                 presetPreset: presetPreset,
118806                 presetManager: _mainPresetIndex,
118807                 presetIndex: presetIndex,
118808                 rendererBackgroundSource: rendererBackgroundSource,
118809                 rendererBackground: rendererBackground,
118810                 rendererFeatures: rendererFeatures,
118811                 rendererMap: rendererMap,
118812                 rendererPhotos: rendererPhotos,
118813                 rendererTileLayer: rendererTileLayer,
118814                 services: services,
118815                 serviceKeepRight: serviceKeepRight,
118816                 serviceImproveOSM: serviceImproveOSM,
118817                 serviceOsmose: serviceOsmose,
118818                 serviceMapillary: serviceMapillary,
118819                 serviceMapRules: serviceMapRules,
118820                 serviceNominatim: serviceNominatim,
118821                 serviceOpenstreetcam: serviceOpenstreetcam,
118822                 serviceOsm: serviceOsm,
118823                 serviceOsmWikibase: serviceOsmWikibase,
118824                 serviceStreetside: serviceStreetside,
118825                 serviceTaginfo: serviceTaginfo,
118826                 serviceVectorTile: serviceVectorTile,
118827                 serviceWikidata: serviceWikidata,
118828                 serviceWikipedia: serviceWikipedia,
118829                 svgAreas: svgAreas,
118830                 svgData: svgData,
118831                 svgDebug: svgDebug,
118832                 svgDefs: svgDefs,
118833                 svgKeepRight: svgKeepRight,
118834                 svgIcon: svgIcon,
118835                 svgGeolocate: svgGeolocate,
118836                 svgLabels: svgLabels,
118837                 svgLayers: svgLayers,
118838                 svgLines: svgLines,
118839                 svgMapillaryImages: svgMapillaryImages,
118840                 svgMapillarySigns: svgMapillarySigns,
118841                 svgMidpoints: svgMidpoints,
118842                 svgNotes: svgNotes,
118843                 svgMarkerSegments: svgMarkerSegments,
118844                 svgOpenstreetcamImages: svgOpenstreetcamImages,
118845                 svgOsm: svgOsm,
118846                 svgPassiveVertex: svgPassiveVertex,
118847                 svgPath: svgPath,
118848                 svgPointTransform: svgPointTransform,
118849                 svgPoints: svgPoints,
118850                 svgRelationMemberTags: svgRelationMemberTags,
118851                 svgSegmentWay: svgSegmentWay,
118852                 svgStreetside: svgStreetside,
118853                 svgTagClasses: svgTagClasses,
118854                 svgTagPattern: svgTagPattern,
118855                 svgTouch: svgTouch,
118856                 svgTurns: svgTurns,
118857                 svgVertices: svgVertices,
118858                 uiFieldDefaultCheck: uiFieldCheck,
118859                 uiFieldOnewayCheck: uiFieldCheck,
118860                 uiFieldCheck: uiFieldCheck,
118861                 uiFieldMultiCombo: uiFieldCombo,
118862                 uiFieldNetworkCombo: uiFieldCombo,
118863                 uiFieldSemiCombo: uiFieldCombo,
118864                 uiFieldTypeCombo: uiFieldCombo,
118865                 uiFieldCombo: uiFieldCombo,
118866                 uiFieldUrl: uiFieldText,
118867                 uiFieldIdentifier: uiFieldText,
118868                 uiFieldNumber: uiFieldText,
118869                 uiFieldTel: uiFieldText,
118870                 uiFieldEmail: uiFieldText,
118871                 uiFieldText: uiFieldText,
118872                 uiFieldAccess: uiFieldAccess,
118873                 uiFieldAddress: uiFieldAddress,
118874                 uiFieldCycleway: uiFieldCycleway,
118875                 uiFieldLanes: uiFieldLanes,
118876                 uiFieldLocalized: uiFieldLocalized,
118877                 uiFieldMaxspeed: uiFieldMaxspeed,
118878                 uiFieldStructureRadio: uiFieldRadio,
118879                 uiFieldRadio: uiFieldRadio,
118880                 uiFieldRestrictions: uiFieldRestrictions,
118881                 uiFieldTextarea: uiFieldTextarea,
118882                 uiFieldWikidata: uiFieldWikidata,
118883                 uiFieldWikipedia: uiFieldWikipedia,
118884                 uiFields: uiFields,
118885                 uiIntro: uiIntro,
118886                 uiPanelBackground: uiPanelBackground,
118887                 uiPanelHistory: uiPanelHistory,
118888                 uiPanelLocation: uiPanelLocation,
118889                 uiPanelMeasurement: uiPanelMeasurement,
118890                 uiInfoPanels: uiInfoPanels,
118891                 uiPaneBackground: uiPaneBackground,
118892                 uiPaneHelp: uiPaneHelp,
118893                 uiPaneIssues: uiPaneIssues,
118894                 uiPaneMapData: uiPaneMapData,
118895                 uiPanePreferences: uiPanePreferences,
118896                 uiSectionBackgroundDisplayOptions: uiSectionBackgroundDisplayOptions,
118897                 uiSectionBackgroundList: uiSectionBackgroundList,
118898                 uiSectionBackgroundOffset: uiSectionBackgroundOffset,
118899                 uiSectionChanges: uiSectionChanges,
118900                 uiSectionDataLayers: uiSectionDataLayers,
118901                 uiSectionEntityIssues: uiSectionEntityIssues,
118902                 uiSectionFeatureType: uiSectionFeatureType,
118903                 uiSectionMapFeatures: uiSectionMapFeatures,
118904                 uiSectionMapStyleOptions: uiSectionMapStyleOptions,
118905                 uiSectionOverlayList: uiSectionOverlayList,
118906                 uiSectionPhotoOverlays: uiSectionPhotoOverlays,
118907                 uiSectionPresetFields: uiSectionPresetFields,
118908                 uiSectionPrivacy: uiSectionPrivacy,
118909                 uiSectionRawMemberEditor: uiSectionRawMemberEditor,
118910                 uiSectionRawMembershipEditor: uiSectionRawMembershipEditor,
118911                 uiSectionRawTagEditor: uiSectionRawTagEditor,
118912                 uiSectionSelectionList: uiSectionSelectionList,
118913                 uiSectionValidationIssues: uiSectionValidationIssues,
118914                 uiSectionValidationOptions: uiSectionValidationOptions,
118915                 uiSectionValidationRules: uiSectionValidationRules,
118916                 uiSectionValidationStatus: uiSectionValidationStatus,
118917                 uiSettingsCustomBackground: uiSettingsCustomBackground,
118918                 uiSettingsCustomData: uiSettingsCustomData,
118919                 uiInit: uiInit,
118920                 uiAccount: uiAccount,
118921                 uiAttribution: uiAttribution,
118922                 uiChangesetEditor: uiChangesetEditor,
118923                 uiCmd: uiCmd,
118924                 uiCombobox: uiCombobox,
118925                 uiCommit: uiCommit,
118926                 uiCommitWarnings: uiCommitWarnings,
118927                 uiConfirm: uiConfirm,
118928                 uiConflicts: uiConflicts,
118929                 uiContributors: uiContributors,
118930                 uiCurtain: uiCurtain,
118931                 uiDataEditor: uiDataEditor,
118932                 uiDataHeader: uiDataHeader,
118933                 uiDisclosure: uiDisclosure,
118934                 uiEditMenu: uiEditMenu,
118935                 uiEntityEditor: uiEntityEditor,
118936                 uiFeatureInfo: uiFeatureInfo,
118937                 uiFeatureList: uiFeatureList,
118938                 uiField: uiField,
118939                 uiFieldHelp: uiFieldHelp,
118940                 uiFlash: uiFlash,
118941                 uiFormFields: uiFormFields,
118942                 uiFullScreen: uiFullScreen,
118943                 uiGeolocate: uiGeolocate,
118944                 uiImproveOsmComments: uiImproveOsmComments,
118945                 uiImproveOsmDetails: uiImproveOsmDetails,
118946                 uiImproveOsmEditor: uiImproveOsmEditor,
118947                 uiImproveOsmHeader: uiImproveOsmHeader,
118948                 uiInfo: uiInfo,
118949                 uiInspector: uiInspector,
118950                 uiIssuesInfo: uiIssuesInfo,
118951                 uiKeepRightDetails: uiKeepRightDetails,
118952                 uiKeepRightEditor: uiKeepRightEditor,
118953                 uiKeepRightHeader: uiKeepRightHeader,
118954                 uiLasso: uiLasso,
118955                 uiLoading: uiLoading,
118956                 uiMapInMap: uiMapInMap,
118957                 uiModal: uiModal,
118958                 uiNotice: uiNotice,
118959                 uiNoteComments: uiNoteComments,
118960                 uiNoteEditor: uiNoteEditor,
118961                 uiNoteHeader: uiNoteHeader,
118962                 uiNoteReport: uiNoteReport,
118963                 uiPopover: uiPopover,
118964                 uiPresetIcon: uiPresetIcon,
118965                 uiPresetList: uiPresetList,
118966                 uiRestore: uiRestore,
118967                 uiScale: uiScale,
118968                 uiSidebar: uiSidebar,
118969                 uiSourceSwitch: uiSourceSwitch,
118970                 uiSpinner: uiSpinner,
118971                 uiSplash: uiSplash,
118972                 uiStatus: uiStatus,
118973                 uiSuccess: uiSuccess,
118974                 uiTagReference: uiTagReference,
118975                 uiToggle: uiToggle,
118976                 uiTooltip: uiTooltip,
118977                 uiVersion: uiVersion,
118978                 uiViewOnOSM: uiViewOnOSM,
118979                 uiViewOnKeepRight: uiViewOnKeepRight,
118980                 uiZoom: uiZoom,
118981                 utilAesEncrypt: utilAesEncrypt,
118982                 utilAesDecrypt: utilAesDecrypt,
118983                 utilArrayChunk: utilArrayChunk,
118984                 utilArrayDifference: utilArrayDifference,
118985                 utilArrayFlatten: utilArrayFlatten,
118986                 utilArrayGroupBy: utilArrayGroupBy,
118987                 utilArrayIdentical: utilArrayIdentical,
118988                 utilArrayIntersection: utilArrayIntersection,
118989                 utilArrayUnion: utilArrayUnion,
118990                 utilArrayUniq: utilArrayUniq,
118991                 utilArrayUniqBy: utilArrayUniqBy,
118992                 utilAsyncMap: utilAsyncMap,
118993                 utilCleanTags: utilCleanTags,
118994                 utilCombinedTags: utilCombinedTags,
118995                 utilDeepMemberSelector: utilDeepMemberSelector,
118996                 utilDetect: utilDetect,
118997                 utilDisplayName: utilDisplayName,
118998                 utilDisplayNameForPath: utilDisplayNameForPath,
118999                 utilDisplayType: utilDisplayType,
119000                 utilDisplayLabel: utilDisplayLabel,
119001                 utilEntityRoot: utilEntityRoot,
119002                 utilEditDistance: utilEditDistance,
119003                 utilEntitySelector: utilEntitySelector,
119004                 utilEntityOrMemberSelector: utilEntityOrMemberSelector,
119005                 utilEntityOrDeepMemberSelector: utilEntityOrDeepMemberSelector,
119006                 utilFastMouse: utilFastMouse,
119007                 utilFunctor: utilFunctor,
119008                 utilGetAllNodes: utilGetAllNodes,
119009                 utilGetSetValue: utilGetSetValue,
119010                 utilHashcode: utilHashcode,
119011                 utilHighlightEntities: utilHighlightEntities,
119012                 utilKeybinding: utilKeybinding,
119013                 utilNoAuto: utilNoAuto,
119014                 utilObjectOmit: utilObjectOmit,
119015                 utilPrefixCSSProperty: utilPrefixCSSProperty,
119016                 utilPrefixDOMProperty: utilPrefixDOMProperty,
119017                 utilQsString: utilQsString,
119018                 utilRebind: utilRebind,
119019                 utilSafeClassName: utilSafeClassName,
119020                 utilSetTransform: utilSetTransform,
119021                 utilSessionMutex: utilSessionMutex,
119022                 utilStringQs: utilStringQs,
119023                 utilTagDiff: utilTagDiff,
119024                 utilTagText: utilTagText,
119025                 utilTiler: utilTiler,
119026                 utilTotalExtent: utilTotalExtent,
119027                 utilTriggerEvent: utilTriggerEvent,
119028                 utilUnicodeCharsCount: utilUnicodeCharsCount,
119029                 utilUnicodeCharsTruncated: utilUnicodeCharsTruncated,
119030                 utilUniqueDomId: utilUniqueDomId,
119031                 utilWrap: utilWrap,
119032                 validationAlmostJunction: validationAlmostJunction,
119033                 validationCloseNodes: validationCloseNodes,
119034                 validationCrossingWays: validationCrossingWays,
119035                 validationDisconnectedWay: validationDisconnectedWay,
119036                 validationFormatting: validationFormatting,
119037                 validationHelpRequest: validationHelpRequest,
119038                 validationImpossibleOneway: validationImpossibleOneway,
119039                 validationIncompatibleSource: validationIncompatibleSource,
119040                 validationMaprules: validationMaprules,
119041                 validationMismatchedGeometry: validationMismatchedGeometry,
119042                 validationMissingRole: validationMissingRole,
119043                 validationMissingTag: validationMissingTag,
119044                 validationOutdatedTags: validationOutdatedTags,
119045                 validationPrivateData: validationPrivateData,
119046                 validationSuspiciousName: validationSuspiciousName,
119047                 validationUnsquareWay: validationUnsquareWay
119048         });
119049
119050         // polyfill requestIdleCallback
119051         window.requestIdleCallback = window.requestIdleCallback ||
119052             function(cb) {
119053                 var start = Date.now();
119054                 return window.requestAnimationFrame(function() {
119055                     cb({
119056                         didTimeout: false,
119057                         timeRemaining: function() {
119058                             return Math.max(0, 50 - (Date.now() - start));
119059                         }
119060                     });
119061                 });
119062             };
119063
119064         window.cancelIdleCallback = window.cancelIdleCallback ||
119065             function(id) {
119066                 window.cancelAnimationFrame(id);
119067             };
119068         window.iD = iD;
119069
119070 }());